Java笔记(24):多线程(02)
Posted 花醉红尘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java笔记(24):多线程(02)相关的知识,希望对你有一定的参考价值。
1、JDK5之后的Lock锁的概述和使用
1 package cn.itcast_01; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 public class SellTicket implements Runnable { 7 8 // 定义票 9 private int tickets = 100; 10 11 // 定义锁对象 12 private Lock lock = new ReentrantLock(); 13 14 @Override 15 public void run() { 16 while (true) { 17 try { 18 // 加锁 19 lock.lock(); 20 if (tickets > 0) { 21 try { 22 Thread.sleep(100); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 System.out.println(Thread.currentThread().getName() 27 + "正在出售第" + (tickets--) + "张票"); 28 } 29 } finally { 30 // 释放锁 31 lock.unlock(); 32 } 33 } 34 } 35 36 }
1 package cn.itcast_01; 2 /* 3 * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁, 4 * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。 5 * 6 * Lock: 7 * void lock(): 获取锁。 8 * void unlock():释放锁。 9 * ReentrantLock是Lock的实现类. 10 */ 11 public class SellTicketDemo { 12 public static void main(String[] args) { 13 // 创建资源对象 14 SellTicket st = new SellTicket(); 15 16 // 创建三个窗口 17 Thread t1 = new Thread(st, "窗口1"); 18 Thread t2 = new Thread(st, "窗口2"); 19 Thread t3 = new Thread(st, "窗口3"); 20 21 // 启动线程 22 t1.start(); 23 t2.start(); 24 t3.start(); 25 } 26 }
2、死锁问题概述和使用
1 package cn.itcast_02; 2 3 public class DieLock extends Thread { 4 5 private boolean flag; 6 7 public DieLock(boolean flag) { 8 this.flag = flag; 9 } 10 11 @Override 12 public void run() { 13 if (flag) { 14 synchronized (MyLock.objA) { 15 System.out.println("if objA"); 16 synchronized (MyLock.objB) { 17 System.out.println("if objB"); 18 } 19 } 20 } else { 21 synchronized (MyLock.objB) { 22 System.out.println("else objB"); 23 synchronized (MyLock.objA) { 24 System.out.println("else objA"); 25 } 26 } 27 } 28 } 29 }
1 package cn.itcast_02; 2 3 /* 4 * 同步的弊端: 5 * A:效率低 6 * B:容易产生死锁 7 * 8 * 死锁: 9 * 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。 10 * 11 * 举例: 12 * 中国人,美国人吃饭案例。 13 * 正常情况: 14 * 中国人:筷子两支 15 * 美国人:刀和叉 16 * 现在: 17 * 中国人:筷子1支,刀一把 18 * 美国人:筷子1支,叉一把 19 */ 20 public class DieLockDemo { 21 public static void main(String[] args) { 22 DieLock dl1 = new DieLock(true); 23 DieLock dl2 = new DieLock(false); 24 25 dl1.start(); 26 dl2.start(); 27 } 28 }
3、生产者消费者问题代码1
1 package cn.itcast_03; 2 3 public class Student { 4 String name; 5 int age; 6 }
1 package cn.itcast_03; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 7 public SetThread(Student s) { 8 this.s = s; 9 } 10 11 @Override 12 public void run() { 13 // Student s = new Student(); 14 s.name = "林青霞"; 15 s.age = 27; 16 } 17 18 }
1 package cn.itcast_03; 2 3 public class GetThread implements Runnable { 4 private Student s; 5 6 public GetThread(Student s) { 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 // Student s = new Student(); 13 System.out.println(s.name + "---" + s.age); 14 } 15 16 }
1 package cn.itcast_03; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 */ 16 public class StudentDemo { 17 public static void main(String[] args) { 18 //创建资源 19 Student s = new Student(); 20 21 //设置和获取的类 22 SetThread st = new SetThread(s); 23 GetThread gt = new GetThread(s); 24 25 //线程类 26 Thread t1 = new Thread(st); 27 Thread t2 = new Thread(gt); 28 29 //启动线程 30 t1.start(); 31 t2.start(); 32 } 33 }
4、生产者消费者题代码2并解决线程安全问题
1 package cn.itcast_04; 2 3 public class Student { 4 String name; 5 int age; 6 }
1 package cn.itcast_04; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 private int x = 0; 7 8 public SetThread(Student s) { 9 this.s = s; 10 } 11 12 @Override 13 public void run() { 14 while (true) { 15 synchronized (s) { 16 if (x % 2 == 0) { 17 s.name = "林青霞";//刚走到这里,就被别人抢到了执行权 18 s.age = 27; 19 } else { 20 s.name = "刘意"; //刚走到这里,就被别人抢到了执行权 21 s.age = 30; 22 } 23 x++; 24 } 25 } 26 } 27 }
1 package cn.itcast_04; 2 3 public class GetThread implements Runnable { 4 private Student s; 5 6 public GetThread(Student s) { 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 while (true) { 13 synchronized (s) { 14 System.out.println(s.name + "---" + s.age); 15 } 16 } 17 } 18 }
1 package cn.itcast_04; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 16 * A:同一个数据出现多次 17 * B:姓名和年龄不匹配 18 * 原因: 19 * A:同一个数据出现多次 20 * CPU的一点点时间片的执行权,就足够你执行很多次。 21 * B:姓名和年龄不匹配 22 * 线程运行的随机性 23 * 线程安全问题: 24 * A:是否是多线程环境 是 25 * B:是否有共享数据 是 26 * C:是否有多条语句操作共享数据 是 27 * 解决方案: 28 * 加锁。 29 * 注意: 30 * A:不同种类的线程都要加锁。 31 * B:不同种类的线程加的锁必须是同一把。 32 */ 33 public class StudentDemo { 34 public static void main(String[] args) { 35 //创建资源 36 Student s = new Student(); 37 38 //设置和获取的类 39 SetThread st = new SetThread(s); 40 GetThread gt = new GetThread(s); 41 42 //线程类 43 Thread t1 = new Thread(st); 44 Thread t2 = new Thread(gt); 45 46 //启动线程 47 t1.start(); 48 t2.start(); 49 } 50 }
5、生产者消费者之等待唤醒机制代码实现
1 package cn.itcast_05; 2 3 public class Student { 4 String name; 5 int age; 6 boolean flag; // 默认情况是没有数据,如果是true,说明有数据 7 }
1 package cn.itcast_05; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 private int x = 0; 7 8 public SetThread(Student s) { 9 this.s = s; 10 } 11 12 @Override 13 public void run() { 14 while (true) { 15 synchronized (s) { 16 //判断有没有 17 if(s.flag){ 18 try { 19 s.wait(); //t1等着,释放锁 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 } 24 25 if (x % 2 == 0) { 26 s.name = "林青霞"; 27 s.age = 27; 28 } else { 29 s.name = "刘意"; 30 s.age = 30; 31 } 32 x++; //x=1 33 34 //修改标记 35 s.flag = true; 36 //唤醒线程 37 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。 38 } 39 //t1有,或者t2有 40 } 41 } 42 }
package cn.itcast_05; public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { if(!s.flag){ try { s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //林青霞---27 //刘意---30 //修改标记 s.flag = false; //唤醒线程 s.notify(); //唤醒t1 } } } }
1 package cn.itcast_05; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 16 * A:同一个数据出现多次 17 * B:姓名和年龄不匹配 18 * 原因: 19 * A:同一个数据出现多次 20 * CPU的一点点时间片的执行权,就足够你执行很多次。 21 * B:姓名和年龄不匹配 22 * 线程运行的随机性 23 * 线程安全问题: 24 * A:是否是多线程环境 是 25 * B:是否有共享数据 是 26 * C:是否有多条语句操作共享数据 是 27 * 解决方案: 28 * 加锁。 29 * 注意: 30 * A:不同种类的线程都要加锁。 31 * B:不同种类的线程加的锁必须是同一把。 32 * 33 * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 34 * 如何实现呢? 35 * 通过Java提供的等待唤醒机制解决。 36 * 37 * 等待唤醒: 38 * Object类中提供了三个方法: 39 * wait():等待 40 * notify():唤醒单个线程 41 * notifyAll():唤醒所有线程 42 * 为什么这些方法不定义在Thread类中呢? 43 * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 44 * 所以,这些方法必须定义在Object类中。 45 */ 46 public class StudentDemo { 47 public static void main(String[] args) { 48 //创建资源 49 Student s = new Student(); 50 51 //设置和获取的类 52 SetThread st = new SetThread(s); 53 GetThread gt = new GetThread(s); 54 55 //线程类 56 Thread t1 = new Thread(st); 57 Thread t2 = new Thread(gt); 58 59 //启动线程 60 t1.start(); 61 t2.start(); 62 } 63 }
线程的状态转换图及常见执行情况
6、线程组的概述和使用
1 package cn.itcast_06; 2 3 public class MyRunnable implements Runnable { 4 5 @Override 6 public void run() { 7 for (int x = 0; x < 100; x++) { 8 System.out.println(Thread.currentThread().getName() + ":" + x); 9 } 10 } 11 12 }
1 package cn.itcast_06; 2 3 /* 4 * 线程组: 把多个线程组合到一起。 5 * 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 6 */ 7 public class ThreadGroupDemo { 8 public static void main(String[] args) { 9 // method1(); 10 11 // 我们如何修改线程所在的组呢? 12 // 创建一个线程组 13 Java多线程学习笔记