黑马程序员技术交流社区
标题: 【阳哥笔记】极速秒杀Java基础之笔记系列—Day14(多线程 )! [打印本页]
作者: 阳哥忠粉 时间: 2015-6-6 19:26
标题: 【阳哥笔记】极速秒杀Java基础之笔记系列—Day14(多线程 )!
5、多线程
5.3 线程间通信
5.3.1 线程间通信涉及的方法
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
等待/唤醒机制涉及的方法:
1. wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
2. notify():唤醒线程池中的一个线程(任何一个都有可能)。
3. notifyAll():唤醒线程池中的所有线程。
P.S.
1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2、必须要明确到底操作的是哪个锁上的线程!
3、wait和sleep区别?
1)wait可以指定时间也可以不指定。sleep必须指定时间。
2)在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
为什么操作线程的方法wait、notify、notifyAll定义在了object类中,因为这些方法是监视器的方法,监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定在object类中。
生产者-消费者问题:
- class Resource{
- private String name ;
- private String sex ;
- private boolean flag = false;
- public synchronized void set(String name,String sex){
- if(flag )
- try{
- this.wait();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- this.name = name;
- this.sex = sex;
- flag = true ;
- this.notify();
- }
-
- public synchronized void out(){
- if(!flag )
- try{
- this.wait();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- System. out.println(name + "..." + sex);
- flag = false ;
- this.notify();
- }
- }
- //输入
- class Input implements Runnable{
- Resource r;
- Input(Resource r){
- this.r = r;
- }
- public void run(){
- int x = 0;
- while(true ){
- if(x == 0){
- r.set( "mike","男" );
- } else{
- r.set( "lili","女" );
- }
- x = (x + 1)%2;
- }
- }
- }
- //输出
- class Output implements Runnable{
- Resource r;
- Output(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.out();
- }
- }
- }
- class ResourceDemo {
- public static void main(String[] args){
- //创建资源
- Resource r = new Resource();
- //创建任务
- Input in = new Input(r);
- Output out = new Output(r);
- //创建线程,执行路径
- Thread t1 = new Thread(in);
- Thread t2 = new Thread(out);
- //开启线程
- t1.start();
- t2.start();
- }
- }
复制代码 运行结果: - class Resource{
- private String name ;
- private int count = 1;
- private boolean flag = false;
- public synchronized void set(String name){
- if(flag )
- try{
- wait();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- this.name = name + count;
- count++;
- System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
- flag = true ;
- notify();
- }
- public synchronized void out(){
- if(!flag )
- try{
- wait();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- flag = false ;
- notify();
- System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
- }
- }
- class Producer implements Runnable{
- private Resource r ;
- Producer(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.set( "烤鸭");
- }
- }
- }
- class Consumer implements Runnable{
- private Resource r ;
- Consumer(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.out();
- }
- }
- }
-
- class ProducerConsumerDemo {
- public static void main(String[] args){
- Resource r = new Resource();
- Producer pro = new Producer(r);
- Consumer con = new Consumer(r);
- Thread t0 = new Thread(pro);
- Thread t1 = new Thread(pro);
- Thread t2 = new Thread(con);
- Thread t3 = new Thread(con);
- t0.start();
- t1.start();
- t2.start();
- t3.start();
- }
- }
复制代码 运行结果:以上代码存在安全问题。
原因分析:
得到以上结果的过程分析如下:
1. 线程Thread-0获取到CPU执行权及锁,生产了烤鸭3298,将flag设置为true。然后,Thread-0又重新获取到CPU执行权,由于flag为true,故执行wait方法,阻塞。Thread-1接着获取到CPU执行权,由于flag为true,故执行wait方法,也阻塞。
2. 线程Thread-3获取到CPU执行权及锁,消费了烤鸭3298,将flag设置为false。然后,线程Thread-0被唤醒,但是并没有获取到锁,而是线程Thread-3接着获取到CPU执行权及锁,然而此时flag为false,所以Thread-3阻塞。下面线程Thread-2接着获取到CPU执行权及锁,然而此时flag为false,所以Thread-2也阻塞。
3. 线程Thread-0获取到CPU执行权及锁,不需要if语句判断,直接生产烤鸭3299,然后又唤醒线程Thread-1获取到CPU执行权及锁,不需要if语句判断,直接生产烤鸭3300。从而造成了烤鸭3299还没有被消费,就直接生产了烤鸭3300的情况。
由于if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。故修改成while判断标记,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。
P.S.
while判断标记+notify会导致死锁的示例:
如果将上面的代码中的if判断标记修改成wile判断标记,就会出现死锁的现象,前2步与原来是一致的。第3步如下:
3. 线程Thread-0获取到CPU执行权及锁,通过了while语句判断,直接生产烤鸭3299,将flag设置为true。然后又唤醒线程Thread-1获取到CPU执行权及锁,没有通过while语句判断,阻塞。线程Thread-0又获取到CPU执行权及锁,通不过while语句判断,也阻塞,此时Thread-0、1、2、3都阻塞,故死锁。
代码:
- class Resource{
- private String name ;
- private int count = 1;
- private boolean flag = false;
- public synchronized void set(String name){
- while(flag )
- try{
- this.wait();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- this.name = name + count;
- count++;
- System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
- flag = true ;
- notifyAll();
- }
- public synchronized void out(){
- while(!flag )
- try{
- this.wait();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- flag = false ;
- notifyAll();
- System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
- }
- }
- class Producer implements Runnable{
- private Resource r ;
- Producer(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.set( "烤鸭");
- }
- }
- }
- class Consumer implements Runnable{
- private Resource r ;
- Consumer(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.out();
- }
- }
- }
- class ProducerConsumerDemo {
- public static void main(String[] args){
- Resource r = new Resource();
- Producer pro = new Producer(r);
- Consumer con = new Consumer(r);
- Thread t0 = new Thread(pro);
- Thread t1 = new Thread(pro);
- Thread t2 = new Thread(con);
- Thread t3 = new Thread(con);
- t0.start();
- t1.start();
- t2.start();
- t3.start();
- }
- }
复制代码 运行结果:
5.3.2 JDK1.5新特性
同步代码块就是对于锁的操作是隐式的。
JDK1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。
使用一个Lock、一个Condition修改上面的多生产者-多消费者问题。
代码:
- import java.util.concurrent.locks.*;
- class Resource{
- private String name ;
- private int count = 1;
- private boolean flag = false;
-
- //创建一个锁对象
- Lock lock = new ReentrantLock();
- //通过已有的锁获取该锁上的监视器对象
- Condition con = lock .newCondition();
- public void set(String name){
- lock.lock();
- try{
- while(flag )
- try{
- con.await();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- this.name = name + count;
- count++;
- System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
- flag = true ;
- con.signalAll();
- }finally{
- lock.unlock();
- }
- }
- public void out(){
- lock.lock();
- try{
- while(!flag )
- try{
- con.await();
- } catch(InterruptedException e){
- e.printStackTrace();
- }
- flag = false ;
- con.signalAll();
- System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
- }finally{
- lock.unlock();
- }
- }
- }
- class Producer implements Runnable{
- private Resource r ;
- Producer(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.set( "烤鸭");
- }
- }
- }
- class Consumer implements Runnable{
- private Resource r ;
- Consumer(Resource r){
- this.r = r;
- }
- public void run(){
- while(true ){
- r.out();
- }
- }
- }
- class ProducerConsumerDemo {
- public static void main(String[] args){
- Resource r = new Resource();
- Producer pro = new Producer(r);
- Consumer con = new Consumer(r);
- Thread t0 = new Thread(pro);
- Thread t1 = new Thread(pro);
- Thread t2 = new Thread(con);
- Thread t3 = new Thread(con);
- t0.start();
- t1.start();
- t2.start();
- t3.start();
- }
- }
复制代码 运行结果: 使用一个Lock、两个Condition修改上面的多生产者-多消费者问题。
~END~
作者: 1014914737 时间: 2015-6-6 21:04
O(∩_∩)O谢谢!!!!!!!!!!!!!1111
作者: itheima_llt 时间: 2015-6-6 22:51
辛苦啦~~谢谢!
作者: qq479470741 时间: 2015-6-7 09:05
阳哥笔记,必属精品,多看多学!
作者: wx_iAuO26mH 时间: 2015-6-7 09:56
学习中 加油
作者: tougboy 时间: 2015-6-7 10:13
看视频的时候就这部分最糊涂 有阳哥的这个笔记 大爱
作者: 307323665 时间: 2015-6-7 16:23
阳哥,辛苦!
作者: 307323665 时间: 2015-6-8 21:40
学习ING!
作者: jife94 时间: 2015-6-10 10:05
谢谢了哈哈哈
作者: jife94 时间: 2015-6-10 10:30
整理的很详细
作者: 小苹果要上树了 时间: 2015-6-10 11:00
很好很强大,给力神贴!
作者: lucien_he 时间: 2015-6-10 12:32
神贴来学习下
作者: wx_iAuO26mH 时间: 2015-6-15 23:01
看完视频,在复习一下笔记
作者: 魏海洲 时间: 2015-6-16 22:58
阳哥笔记,必属精品,多看多学!
作者: wenweishan2015 时间: 2015-6-22 22:22
赞!!!
作者: Huan220_欢 时间: 2015-6-25 09:55
辛苦了,学习中........加油!
作者: Baymaxman 时间: 2015-6-25 12:15
学习了~
作者: wenweishan2015 时间: 2015-6-25 18:14
赞!!!
作者: Jackie7 时间: 2015-6-25 20:15
顶一个!
作者: 定格在永远 时间: 2015-6-28 16:36
受教。。。。
作者: 飘影 时间: 2015-6-28 19:22
刚看完多线程的教程!
作者: Z、c 时间: 2015-6-29 19:46
总结的很全面,赞赞赞!!!
作者: Z、c 时间: 2015-7-1 09:50
顶完再看!!!
作者: 牵着蜗牛去逛街 时间: 2015-7-1 21:28
努力学习中
作者: 走在这里 时间: 2015-7-1 21:53
黑马的老师都是精英的精英谢谢老师们
作者: heisedelangzi 时间: 2015-7-2 21:29
好难,看不懂。
作者: 王勋亮 时间: 2015-8-3 09:19
每天都会看!!!!很好!!!!
作者: Swornf、时年 时间: 2015-8-4 08:28
赞!顶黑马!!
作者: liuao2010 时间: 2015-8-4 19:29
赞赞赞~~~~~
作者: 放心飞 时间: 2015-8-28 15:08
看完这一节受益匪浅,加油,再接再厉!
作者: 放心飞 时间: 2015-8-28 16:51
一直在努力,加油吧!学习受益匪浅.
作者: q370349954 时间: 2015-8-28 17:48
真心给力。帮助总结
作者: kime 时间: 2015-9-5 10:11
黑马黑马,加油
作者: 1975532882 时间: 2015-9-19 21:37
顶,,,,,,,,,,,,,,,,,,,,,,
作者: Smilexs 时间: 2015-9-27 12:24
辛苦了,这个挺难消化的。
作者: 1975532882 时间: 2015-10-24 16:17
牛叉,,,,,,,,,,
作者: 1975532882 时间: 2015-10-24 19:34
牛叉,,,,,,,
作者: q291793758 时间: 2015-12-11 22:44
线程迷糊了。
作者: dengwenjing58 时间: 2015-12-12 11:41
谢谢分享
作者: 铃铃铃铃铃锋 时间: 2016-1-15 14:41
由衷感谢分享
作者: 小明啊 时间: 2016-1-15 21:01
好东西,收藏啊
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |