目录:
一、线程间的通信示例 返目录回
多个线程在处理同一资源,任务却不同。
假设有一堆货物,有一辆车把这批货物往仓库里面运,另外一辆车把前一辆车运进仓库的货物往外面运。这里货物就是同一资源,但是两辆车的任务却不同,一个是往里运,一个是往外运。
下面举例子来逐步展示线程间通信:首先建立一个Person类。包含 name 和 sex 属性, 我们建立一个线程输入一个对象(即输入一个name和sex), 另一个线程输出该对象(即输出该对象的name 和 sex).
1 package thread.demo; 2 3 class Person 4 { 5 PRivate String name; 6 private String sex; 7 8 public String getName() 9 {10 return name;11 }12 public void setName(String name) 13 {14 this.name = name;15 }16 public String getSex() 17 {18 return sex;19 }20 public void setSex(String sex) 21 {22 this.sex = sex;23 }24 25 }26 //输入27 class Input implements Runnable28 {29 private Person r;30 31 Input(Person r)32 {33 this.r = r;34 }35 36 public void run()37 {38 int x = 0;39 while(true)//这里加无限循环是为了方便后面观察现象40 {41 if (x == 0)42 {43 r.setName("Mike");44 r.setSex("Male");45 }46 else47 {48 r.setName("Lucy");49 r.setSex("Female");50 }51 x = (x + 1) % 2; //变换x的值,使得切换输入不同的对象52 }53 }54 }55 56 class Output implements Runnable57 {58 private Person r;59 60 Output(Person r)61 {62 this.r = r;63 }64 65 public void run()66 {67 while(true)//这里加无限循环是为了方便后面观察现象68 {69 System.out.println(r.getName() + "..." + r.getSex());70 }71 }72 }73 public class MultithreadDemo_1 74 {75 76 /**77 * @param args78 */79 public static void main(String[] args) 80 {81 //建立共享数据Person r,输入和输出都操作对象 r82 Person r = new Person();83 Input in = new Input(r);84 Output out = new Output(r);85 86 //建立两个线程,分别执行输入任务和输出任务87 Thread t1 = new Thread(in);88 Thread t2 = new Thread(out);89 90 //开启线程91 t1.start();92 t2.start();93 }94 }View Code
执行结果就是一直不断输出,会发现有如下类似现象:
问题来了,程序中明明Mike的sex是Male,Lucy是Female,却出现了上面图片中“诡异”的的现象,这当然是线程安全问题!来分析一下:
在上一篇博文Java多线程技术学习笔记(一)中分析了线程安全问题产生的原因:
回头看我们的代码,共享数据Person r被两个线程操作,满足第一条;操作 r 的代码就是run方法里面的代码,看到第36行开始的run方法确实有很多条,满足第二个条件!所以出现上述诡异输出其实是很正常的现象!具体到上述代码,造成原因:
分析了原因,就来解决问题,那就是前一篇博文笔记里面说的同步:
1 //输入 2 class Input implements Runnable 3 { 4 private Person r; 5 6 Input(Person r) 7 { 8 this.r = r; 9 }10 11 public void run()12 {13 int x = 0;14 while(true)//这里加无限循环是为了方便后面观察现象15 {16 synchronized(r)//同步代码块的锁可以使用任意对象,只要保证多个线程使用的是同一个锁即可17 {18 if (x == 0)19 {20 r.setName("Mike");21 r.setSex("Male");22 }23 else24 {25 r.setName("Lucy");26 r.setSex("Female");27 }28 }29 x = (x + 1) % 2; //变换x的值,使得切换输入不同的对象30 }31 }32 }33 34 class Output implements Runnable35 {36 private Person r;37 38 Output(Person r)39 {40 this.r = r;41 }42 43 public void run()44 {45 while(true)//这里加无限循环是为了方便后面观察现象46 {47 synchronized(r)48 {49 System.out.println(r.getName() + "..." + r.getSex());50 }51 }52 }53 }View Code
多次运行之后可以验证,输出正常:
但是注意到,输出是连续一堆Lucy...Female,然后连续一堆Mike...Male,原因很简单:一旦切换到输出线程,该线程不可能只执行一次,一下输出多次,因为name 和 sex 由于同步的缘故,要么是Lucy...Female,要么是Mike...Male,一输出就是一片相同的Lucy或者Mike. 为了展示多线程间的通信,现在要实现的是,输入线程输入一个name和sex,就立马在输出线程输出,然后再输入一个,再输出一个,如此交替!注意输入和输出是在不同线程里面执行的!所以就需要线程间通信,即输入线程输了一个name和sex,就不在继续输入,而是去通知输出线程输出一下刚才输入的name和sex,输出一次之后,也不再继续输出,而是去通知输入线程继续输入新的内容,输入线程和输出线程如此交替... 这就是所谓的“等待唤醒机制”。
二、等待唤醒机制返目录回
要达到上面所说的输入和输出线程交替执行,需要设置一个标志位,根据标志位来判断到底是该执行输出还是输出!
涉及的方法:
翻译过来意思就是:该方法会导致当前线程等待,直到其他线程调用了此线程的notify或者notifyAll方法。 注意到wait方法会抛出异常,所以在面我们的代码中加入了try/catch
这些方法都必须定义在同步中。因为这些方法是用于操作线程状态的方法,所以必须要明确到底操作的是哪个锁上的线程。
注意到上述操作线程的方法都是放在Object类中,这是因为方法都是同步锁的方法。而锁可以是任意对象,任意的对象都可以调用的方法一定定义在Object类中。
代码思路:初始化标志位-->输入线程输入-->更改标志位-->唤醒输出线程-->输出线程输出-->更该标志位-->唤醒输入线程-->输入线程输入--> ...
代码改动如下:
1 package thread.demo; 2 /* 3 * 等待/唤醒机制 4 */ 5 class Person 6 { 7 private String name; 8 private String sex; 9 boolean full = false;//标志位,代表着是否已经更新了name和sex 10 11 public String getName() 12 { 13 return name; 14 } 15 public void setName(String name) 16 { 17 this.name = name; 18 } 19 public String getSex() 20 { 21 return sex; 22 } 23 public void setSex(String sex) 24 { 25 this.sex = sex; 26 } 27 28 } 29 //输入 30 class Input implements Runnable 31 { 32 private Person r; 33 34 Input(Person r) 35 { 36 this.r = r; 37 } 38 39 public void run() 40 { 41 int x = 0; 42 while(true)//这里加无限循环是为了方便后面观察现象 43 { 44 synchronized(r)//同步代码块的锁可以使用任意对象,只要保证多个线程使用的是同一个锁即可 45 { 46 if (r.full) 47 { 48 // 如果full标志位为真 49 // r锁的wait方法让线程冻结,在线程池中等待,就不执行后面的输入name和sex的语句 50 try { 51 r.wait();//注意调用wait方法要明确锁 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 //如果标志位为假,就执行下面语句输入name和sex 57 if (x == 0) 58 { 59 r.setName("Mike"); 60 r.setSex("Male"); 61 } 62 else 63 { 64 r.setName("Lucy"); 65 r.setSex("Female"); 66 } 67 //输入了一个对象,即一对name和sex之后,将标志位置为真 68 r.full = true; 69 //然后通知输出线程(即唤醒输出线程)来输出刚输入的内容 70 r.notify(); 71 } 72 x = (x + 1) % 2; //变换x的值,使得切换输入不同的对象 73 } 74 } 75 } 76 77 class Output implements Runnable 78 { 79 private Person r; 80 81 Output(Person r) 82 { 83 this.r = r; 84 } 85 86 public void run() 87 { 88 while(true)//这里加无限循环是为了方便后面观察现象 89 { 90 synchronized(r) 91 { 92 //如果输入线程还没有输入内容,输出线程就等待 93 if (!r.full) 94 { 95 try { 96 r.wait(); 97 } catch (InterruptedException e) { 98 e.printStackTrace(); 99 }100 }101 //如果已经输入了内容,就直接输出102 System.out.println(r.getName() + "..." + r.getSex());103 //输出完了之后,将标志位置为false,表明刚才的内容应经输出了104 r.full = false;105 //然后通知输入线程再输入新内容106 r.notify();107 }108 }109 }110 }111 public class MultithreadDemo_1 112 {113 114 /**115 * @param args116 */117 public static void main(String[] args) 118 {119 //建立共享数据Person r,输入和输出都操作对象 r120 Person r = new Person();121 Input in = new Input(r);122 Output out = new Output(r);123 124 //建立两个线程,分别执行输入任务和输出任务125 Thread t1 = new Thread(in);126 Thread t2 = new Thread(out);127 128 //开启线程129 t1.start();130 t2.start();131 }132 }View Code
运行结果:
达到了预期。
三、等待唤醒机制的优化返目录回
再考虑上面写的代码,其实并不好,同步的目的是为了防止某个线程对name赋值以后,还没来得及对sex赋值时,其他线程就切了进来!所以需要同步的代码就是赋值的两行:
59,60行代码与64,65行代码代码功能重复,所以优化代码如下:
1 package thread.demo; 2 /* 3 * 等待/唤醒机制 4 */ 5 class Person 6 { 7 private String name; 8 private String sex; 9 private boolean full = false;//标志位,代表着是否已经更新了name和sex 10 11 public String getName() 12 { 13 return name; 14 } 15 public void setName(String name) 16 { 17 this.name = name; 18 } 19 public String getSex() 20 { 21 return sex; 22 } 23 public void setSex(String sex) 24 { 25 this.sex = sex; 26 } 27 28 public synchronized void set(String name, String sex) 29 { 30 if (full) 31 { 32 try 33 { 34 this.wait(); //注意同步函数的锁是this,所以这里调用this的wait方法 35 } 36 catch (InterruptedException e) 37 { 38 e.printStackTrace(); 39 } 40 } 41 42 this.name = name; 43 this.sex = sex; 44 full = true; 45 notify(); 46 } 47 48 public synchronized void show() 49 { 50 if (!full) 51 { 52 try 53 { 54 this.wait(); //注意同步函数的锁是this,所以这里调用this的wait方法 55 } 56 catch (InterruptedException e) 57 { 58 e.printStackTrace(); 59 } 60 } 61 //如果已经输入了内容,就直接输出 62 System.out.println(name + "..." + sex); 63 full = false; 64 notify(); 65 } 66 } 67 //输入 68 class Input implements Runnable 69 { 70 private Person r; 71 72 Input(Person r) 73 { 74 this.r = r; 75 } 76 77 public void run() 78 { 79 int x = 0; 80 while(true)//这里加无限循环是为了方便后面观察现象 81 { 82 if (x == 0) 83 { 84 r.set("Mike", "Male"); 85 } 86 else 87 { 88 r.set("Lucy", "Female"); 89 } 90 x = (x + 1) % 2; //变换x的值,使得切换输入不同的对象 91 } 92 } 93 } 94 95 class Output implements Runnable 96 { 97 private Person r; 98 99 Output(Person r)100 {101 this.r = r;102 }103 104 public void run()105 {106 while(true)//这里加无限循环是为了方便后面观察现象 107 {108 r.show();109 }110 }111 }112 public class MultithreadDemo_1 113 {114 115 /**116 * @param args117 */118 public static void main(String[] args) 119 {120 //建立共享数据Person r,输入和输出都操作对象 r121 Person r = new Person();122 Input in = new Input(r);123 Output out = new Output(r);124 125 //建立两个线程,分别执行输入任务和输出任务126 Thread t1 = new Thread(in);127 Thread t2 = new Thread(out);128 129 //开启线程130 t1.start();131 t2.start();132 }133 }View Code
功能与前面代码其实是一样的。
四、线程间通信经典问题:多生产者多消费者问题返目录回
这个问题很直接:就是一堆生产者生产产品,同时一堆消费者在消费产品!这一堆生产者和消费者对应程序中的多个线程,而产品就对应着这一堆线程共同操作的资源或者叫做共享数据。
我们想达到的目的是生产一件商品,消费一件,生产消费彼此交替!
首先来看一个生产者,一个消费者的例子,即生产一个就消费一个:
1 package thread.demo; 2 3 /* 4 * 生产者消费者问题 5 */ 6 class Product 7 { 8 private String name;// 产品名称 9 private int number = 1; // 产品编号 10 private boolean notEmpty = false; 11 public synchronized void produce(String name) 12 { 13 //如果有产品,可以停止生产一会 14 if (notEmpty) 15 { 16 try 17 { 18 this.wait(); 19 } 20 catch (InterruptedException e) 21 { 22 e.printStackTrace(); 23 } 24 } 25 // 如果没有产品,就无需等待,直接生产 26 // 生产的产品名称 27 this.name = name + number; 28 // 编号递增 29 number++; 30 // 输出生产的产品信息:线程名(对应在某一个生产者)+产品名 31 System.out.println(Thread.currentThread().getName() + " 生产出: " + this.name); 32 // 生产完了以后,就有了产品 33 notEmpty = true; 34 //通知消费者来消费 35 notify(); 36 } 37 38 public synchronized void consume() 39 { 40 // 如果没有产品,无法消费,等待 41 if (!notEmpty) 42 { 43 try 44 { 45 this.wait(); 46 } 47 catch (InterruptedException e) 48 { 49 e.printStackTrace(); 50 } 51 } 52 //打印产品被消费的信息:线程名(对应着某一个消费者) + 产品名 53 System.out.println(Thread.currentThread().getName() + "消费了:-> " + this.name); 54 //消费完了,通知生产者 55 notEmpty = false; 56 notify(); 57 } 58 } 59 60 // 创建生产者线程 61 class Producer implements Runnable 62 { 63 private Product p; 64 Producer(Product p) 65 { 66 this.p = p; 67 } 68 69 public void run() 70 { 71 while (true) 72 { 73 p.produce("bread"); // 假如生产面包 74 } 75 } 76 77 } 78 79 //创建消费者线程 80 class Consumer implements Runnable 81 { 82 private Product p; 83 Consumer(Product p) 84 { 85 this.p = p; 86 } 87 88 public void run() 89 { 90 while (true)//消费者消费 91 { 92 p.consume(); 93 } 94 } 95 96 } 97 public class ProducerConsumerDemo { 98 99 public static void main(String[] args) 100 {101 // 创建共享资源102 Product p = new Product();103 // 创建两个线程:生产和消费104 Producer producer = new Producer(p);105 Consumer consumer = new Consumer(p);106 Thread t1 = new Thread(producer);107 Thread t2 = new Thread(consumer);108 t1.start();109 t2.start();110 }111 }View Code
运行结果:
这其实就是前面等待唤醒机制的另一种展示!下面在此代码的基础上改成多生产者,多消费者的示例:
1 public class ProducerConsumerDemo { 2 3 public static void main(String[] args) 4 { 5 // 创建共享资源 6 Product p = new Product(); 7 // 两个生产者,两个消费者 8 Producer producer = new Producer(p); 9 Consumer consumer = new Consumer(p);10 Thread t0 = new Thread(producer);11 Thread t1 = new Thread(producer);12 13 Thread t2 = new Thread(consumer);14 Thread t3 = new Thread(consumer);15 16 t0.start();17 t1.start();18 t2.start();19 t3.start();20 }21 }View Code
多次运行,会出现下面类似结果:
产生的问题:
显然这些问题都是不合理的,问题肯定出在多线程上,下面分分析。为了方便叙述,代码全部整理如下:
1 package thread.demo; 2 3 /* 4 * 生产者消费者问题 5 */ 6 class Product 7 { 8 private String name;// 产品名称 9 private int number = 1; // 产品编号 10 private boolean notEmpty = false; 11 public synchronized void produce(String name) 12 { 13 //如果有产品,可以停止生产一会 14 if (notEmpty) 15 { 16 try 17 { 18 this.wait(); 19 } 20 catch (InterruptedException e) 21 { 22 e.printStackTrace(); 23 } 24 } 25 // 如果没有产品,就无需等待,直接生产 26 // 生产的产品名称 27 this.name = name + number; 28 // 编号递增 29 number++; 30 // 输出生产的产品信息:线程名(对应在某一个生产者)+产品名 31 System.out.println(Thread.currentThread().getName() + " 生产出: " + this.name); 32 // 生产完了以后,就有了产品 33 notEmpty = true; 34 //通知消费者来消费 35 notify(); 36 } 37 38 public synchronized void consume() 39 { 40 // 如果没有产品,无法消费,等待 41 if (!notEmpty) 42 { 43 try 44 { 45 this.wait(); 46 } 47 catch (InterruptedException e) 48 { 49 e.printStackTrace(); 50 } 51 } 52 //打印产品被消费的信息:线程名(对应着某一个消费者) + 产品名 53 System.out.println(Thread.currentThread().getName() + "消费了:-> " + this.name); 54 //消费完了,通知生产者 55 notEmpty = false; 56 notify(); 57 } 58 } 59 60 // 创建生产者线程 61 class Producer implements Runnable 62 { 63 private Product p; 64 Producer(Product p) 65 { 66 this.p = p; 67 } 68 69 public void run() 70 { 71 while (true) 72 { 73 p.produce("bread"); // 假如生产面包 74 } 75 } 76 77 } 78 79 //创建消费者线程 80 class Consumer implements Runnable 81 { 82 private Product p; 83 Consumer(Product p) 84 { 85 this.p = p; 86 } 87 88 public void run() 89 { 90 while (true)//消费者消费 91 { 92 p.consume(); 93 } 94 } 95 96 } 97 public class ProducerConsumerDemo { 98 99 public static void main(String[] args) 100 {101 // 创建共享资源102 Product p = new Product();103 // 两个生产者,两个消费者104 Producer producer = new Producer(p);105 Consumer consumer = new Consumer(p);106 Thread t0 = new Thread(producer);107 Thread t1 = new Thread(producer);108 109 Thread t2 = new Thread(consumer);110 Thread t3 = new Thread(consumer);111 112 t0.start();113 t1.start();114 t2.start();115 t3.start();116 }117 }View Code
按照上面分析,是不会出现上述运行现象的,于是进一步分析:
把程序中的四个线程画图分析如下:
其中双向箭头表示所连接的两线程可以互相唤醒。假如存在A箭头或者B箭头连续执行的情况,就会出现连续生产多个产品而不消费的情况,或者连续消费同一个产品而不生产的情况。很显然只要发生中间四个箭头的情况,就会生产一个,消费一个,从而满足我们的目的。所以解决的原因显而易见:防止A和B情况的发生,即生产者线程不能唤醒生产者线程,只能唤醒消费者线程,而消费者线程也只允许唤醒生产者线程。
五、多生产多消费问题的解决返目录回
上面分析到,t0唤醒t1后,由于t1从wait处醒过来不判断标记就继续往下执行,就出现了多生产,试想如果t1在被唤醒之后判断一下标记,t1会再次等待,即使t0再次过来也再次判断标记,也会一直等待,而不会去连续多次生产了,所以把14行和41行的 if 改为while,这样,每一个线程被唤醒之后就必须重新判断标记,改动之后运行结果如下:
现象就是运行若干次程序停止了,即就是在Java多线程技术学习笔记(一)提到的死锁现象,分析原因如下:
虽然线程每次都重新判断了标记,但是会出现上面死锁的现象,考虑到上面说到notifyAll方法还没有出场过,试着把notify改为notifyAll:
1 package thread.demo; 2 3 /* 4 * 生产者消费者问题 5 */ 6 class Product 7 { 8 private String name;// 产品名称 9 private int number = 1; // 产品编号 10 private boolean notEmpty = false; 11 public synchronized void produce(String name) 12 { 13 //如果有产品,可以停止生产一会 14 while (notEmpty) 15 { 16 try 17 { 18 this.wait(); 19 } 20 catch (InterruptedException e) 21 { 22 e.printStackTrace(); 23 } 24 } 25 // 如果没有产品,就无需等待,直接生产 26 // 生产的产品名称 27 this.name = name + number; 28 // 编号递增 29 number++; 30 // 输出生产的产品信息:线程名(对应在某一个生产者)+产品名 31 System.out.println(Thread.currentThread().getName() + " 生产出: " + this.name); 32 // 生产完了以后,就有了产品 33 notEmpty = true; 34 //通知其他线程 35 //notify(); 36 notifyAll(); 37 38 } 39 40 public synchronized void consume() 41 { 42 // 如果没有产品,无法消费,等待 43 while (!notEmpty) 44 { 45 try 46 { 47 this.wait(); 48 } 49 catch (InterruptedException e) 50 { 51 e.printStackTrace(); 52 } 53 } 54 //打印产品被消费的信息:线程名(对应着某一个消费者) + 产品名 55 System.out.println(Thread.currentThread().getName() + "消费了:-> " + this.name); 56 //消费完了,通知其他线程 57 notEmpty = false; 58 //notify(); 59 notifyAll(); 60 } 61 } 62 63 // 创建生产者线程 64 class Producer implements Runnable 65 { 66 private Product p; 67 Producer(Product p) 68 { 69 this.p = p; 70 } 71 72 public void run() 73 { 74 while (true) 75 { 76 p.produce("bread"); // 假如生产面包 77 } 78 } 79 80 } 81 82 //创建消费者线程 83 class Consumer implements Runnable 84 { 85 private Product p; 86 Consumer(Product p) 87 { 88 this.p = p; 89 } 90 91 public void run() 92 { 93 while (true)//消费者消费 94 { 95 p.consume(); 96 } 97 } 98 99 }100 public class ProducerConsumerDemo {101 102 public static void main(String[] args) 103 {104 // 创建共享资源105 Product p = new Product();106 // 两个生产者,两个消费者107 Producer producer = new Producer(p);108 Consumer consumer = new Consumer(p);109 Thread t0 = new Thread(producer);110 Thread t1 = new Thread(producer);111 112 Thread t2 = new Thread(consumer);113 Thread t3 = new Thread(consumer);114 115 t0.start();116 t1.start();117 t2.start();118 t3.start();119 }120 }View Code
多次运行会发现,结果正是我们最初想要的:生产一个就消费一个!
原因很简单:notifyAll会唤醒线程池中所有的线程,假如t0生产了一次,就会唤醒t1,t2,t3,如果t1抢到cpu执行权就会判断标记等待,然后醒着的消费线程抢到执行权, 就去消费一次,然后唤醒所有等待的线程,同样,因为消费了一次,只要消费线程抢到cpu执行权就会根据标记去等待,生产者线程抢到cpu执行权就会判断标记,然后去生产,如此循环!至此,问题得到解决!
六、JDK1.5之后的新加锁方式返目录回
在API文档中有一个Lock接口:
翻译:Lock 实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。
方法如下:
lock方法获取锁,unlock方法释放锁。
以前使用同步代码块和一个对象相结合的方式,实现线程同步,有了Lock接口以后,可以通过一个锁对象完成线程的同步。
使用Lock接口的一个已知实现类ReentrantLock来改写上面的多生产多消费程序,首先看看ReentrantLock的API文档里写的怎么用这个类:
而Lock接口的描述里面还提到:Lock 可以支持多个相关的 Condition 对象,Condition的API:
翻译:Condition将Object锁的监视器方法:wait,notify和notifyAll分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合,为每个对象提供多个等待set.其中,Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。
大致意思就是把这些锁的方法wait,notify和notifyAll封装在Condition中,而锁Lock和Condition是什么关系呢?在上面Lock方法的截图中:
即newCondition方法返回绑定到此Lock实例的新 Condition实例,所以Lock和Condition就是通过这个方法绑定一起,然后就能通过Condition实例调用与该锁想关的wait,notify和notifyAll方法。
但是注意wait,notify和notifyAll方法在Condition中的名称有所改变,但是功能是一样的:
好了,根据上面的知识,得出修改的代码:
1 package thread.demo; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurrent.locks.Lock; 5 import java.util.concurrent.locks.ReentrantLock; 6 7 /* 8 * 生产者消费者问题 9 */ 10 class NewProduct 11 { 12 private String name;// 产品名称 13 private int number = 1; // 产品编号 14 private boolean notEmpty = false; 15 16 // 创建一个锁对象 17 Lock lock = new ReentrantLock(); 18 19 // 通过已有的锁获取该锁上的监视器对象 20 Condition c = lock.newCondition(); 21 public void produce(String name) 22 { 23 lock.lock(); 24 try 25 { 26 //如果有产品,可以停止生产一会 27 while (notEmpty) 28 { 29 /* 30 try 31 { 32 this.wait(); 33 } 34 catch (InterruptedException e) 35 { 36 e.printStackTrace(); 37 } 38 */ 39 40 try { 41 c.await(); 42 } catch (InterruptedException e) { 43 e.printStackTrace(); 44 } 45 } 46 // 如果没有产品,就无需等待,直接生产 47 // 生产的产品名称 48 this.name = name + number; 49 // 编号递增 50 number++; 51 // 输出生产的产品信息:线程名(对应在某一个生产者)+产品名 52 System.out.println(Thread.currentThread().getName() + " 生产出: " + this.name); 53 // 生产完了以后,就有了产品 54 notEmpty = true; 55 //通知其他线程 56 //notify(); 57 //notifyAll(); 58 c.signalAll(); 59 } 60 finally 61 { 62 lock.unlock(); 63 } 64 } 65 66 public void consume() 67 { 68 lock.lock(); 69 try 70 { 71 // 如果没有产品,无法消费,等待 72 while (!notEmpty) 73 { 74 /* 75 try 76 { 77 this.wait(); 78 } 79 catch (InterruptedException e) 80 { 81 e.printStackTrace(); 82 } 83 */ 84 try { 85 c.await(); 86 } catch (InterruptedException e) { 87 e.printStackTrace(); 88 } 89 } 90 //打印产品被消费的信息:线程名(对应着某一个消费者) + 产品名 91 System.out.println(Thread.currentThread().getName() + "消费了:-> " + this.name); 92 //消费完了,通知其他线程 93 notEmpty = false; 94 //notify(); 95 //notifyAll(); 96 c.signalAll(); 97 } 98 finally 99 {100 lock.unlock();101 }102 }103 }104 105 // 创建生产者线程106 class NewProducer implements Runnable107 {108 private NewProduct p;109 NewProducer(NewProduct p2)110 {111 this.p = p2;112 }113 114 public void run()115 {116 while (true)117 {118 p.produce("bread"); // 假如生产面包119 }120 }121 122 }123 124 //创建消费者线程125 class NewConsumer implements Runnable126 {127 private NewProduct p;128 NewConsumer(NewProduct p)129 {130 this.p = p;131 }132 133 public void run()134 {135 while (true)//消费者消费136 {137 p.consume();138 }139 }140 141 }142 public class LockDemo {143 144 public static void main(String[] args) 145 {146 // 创建共享资源147 NewProduct p = new NewProduct();148 // 两个生产者,两个消费者149 NewProducer NewProducer = new NewProducer(p);150 NewConsumer NewConsumer = new NewConsumer(p);151 Thread
新闻热点
疑难解答