三、线程间的通信

下面程序会有什么问题

package examination;

import java.util.ArrayList;
import java.util.List;

/**
 * taobao examination
 * 实现一个容器,提供两个方法,add和size
 * 写两个线程,线程1添加10个元素到容器中,
 * 线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 * @author xzq
 */
public class Examination01 {

    public static void main(String[] args) {
        final Box_1 box=new Box_1();

        new Thread("线程1"){

            @Override
            public void run() {
                try {
                    for(int i=1;i<=10;i++){
                        box.add(new Object());
                        System.out.println("线程1:add第"+i+"个元素");
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();


        new Thread("线程2"){

            @Override
            public void run() {
                while(true){
                    if(box.size()==5){
                        break;
                    }
                }
                System.out.println("线程2执行完毕!");
            }
        }.start();
    }

}

/**
 * 这样实现有什么问题?
 * @author xzq
 */
class Box_1{

    private List<Object> box=new ArrayList<>();

    public void add(Object element){
        box.add(element);
    }

    public int size(){
        return box.size();
    }

}

在这里插入图片描述
线程2不会得到通知,不会结束,
线程1不通知我 我自己获取 也可以结束,加入下面代码
在这里插入图片描述
在这里插入图片描述

2、解决问题 为对象添加volatile
private volatile List box=new ArrayList<>();

package examination;


import java.util.ArrayList;
import java.util.List;

/**
 * taobao examination
 * 实现一个容器,提供两个方法,add和size
 * 写两个线程,线程1添加10个元素到容器中,
 * 线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 * @author xzq
 */
public class Examination02 {

    public static void main(String[] args) {
        final Box_2 box=new Box_2();

        new Thread("线程1"){

            @Override
            public void run() {
                try {
                    for(int i=1;i<=10;i++){
                        box.add(new Object());
                        System.out.println("线程1:add第"+i+"个元素");
                        Thread.sleep(1_000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();


        new Thread("线程2"){

            @Override
            public void run() {
                try {
                    while(true){
                        if(box.size()==5){
                            Thread.sleep(2_000);
                            System.out.println("线程2:我看到容器中已经有5个元素啦!!!");
                            break;
                        }
                    }
                    System.out.println("线程2:执行完毕!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

}

/**
 * 加上volatile使box的修改可以得到通知
 * 这样实现有什么问题?
 *
 * 1、由于add和size没有加锁,那么就不具备互斥性,
 * 有可能会出现当线程2判断完size==5后,线程切换了
 * 然后线程1又加了一次已经到6了,然后在切换回线程2
 * 此时,线程2才break,这样就与原逻辑不符。
 *
 * 2、用死循环的方式十分浪费CPU的资源
 * @author xzq
 */
class Box_2{

    private volatile List<Object> box=new ArrayList<>();

    public void add(Object element){
        box.add(element);
    }

    public int size(){
        return box.size();
    }

}

在这里插入图片描述
3、使用wait和notify方法
wait()会释放锁,而nodify()不会释放锁。
线程2wait()后释放了锁 线程1进入,但线程1执行到size=5时nodify()了,但线程2并没有继续执行而是等线程1结束才开始执行 说明nodify()不会释放锁

package examination;

import java.util.ArrayList;
import java.util.List;

/**
 * taobao examination
 * 实现一个容器,提供两个方法,add和size
 * 写两个线程,线程1添加10个元素到容器中,
 * 线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 *
 * 这里使用wait()和notify()方法实现,
 * 注意:wait()会释放锁,而nodify()不会释放锁。
 *
 * 该例中线程2必须比线程1先启动,才能保持监听状态
 * 因为必须先wait,才有nodify。
 *
 * 但是本程序的输出还是没有达到预期效果:
 * size=5的时候线程2没有继续往下执行,而是
 * 等到线程1执行完毕后才往下执行的,因为
 * nodify是不释放锁的,而wait被nodify后是需要
 * 重新持有锁的,但是现在这把锁被线程1持有着
 * 所以只能等到线程1离开同步代码块时,线程2
 * 才能继续往下执行。

 * @author xzq
 */
public class Examination03 {

    public static void main(String[] args) {
        final Box_3 box=new Box_3();
        final Object obj=new Object();

        new Thread("线程2"){

            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        System.out.println("线程2:开始执行");
                        //4秒后线程1才打印 说明4秒后线程2中wait()释放了锁
                        Thread.sleep(4000);
                        if(box.size()!=5){
                            obj.wait();
                        }
                        System.out.println("线程2:执行完毕!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();



        new Thread("线程1"){

            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        System.out.println("线程1:开始执行");
                        for(int i=1;i<=10;i++){
                            box.add(new Object());
                            System.out.println("线程1:add第"+i+"个元素");
                            if(box.size()==5){
                                obj.notify();
                            }
                            Thread.sleep(1_000);
                        }
                        System.out.println("线程1:执行完毕!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

}

/**
 * 加上volatile后使box的修改可以得到通知
 *
 * @author Peter
 */
class Box_3{

    private volatile List<Object> box=new ArrayList<>();

    public void add(Object element){
        box.add(element);
    }

    public int size(){
        return box.size();
    }

}

在这里插入图片描述
4、优化wait和notify方法

解决方案:

  • 线程1在nodify后必须要释放锁,那么就得wait了,然后线程2就可以获取到锁
  • 然后线程2被唤醒后也必须再nodify通知线程2,虽然使用nodify唤醒了线程1,但线程2并没有释放锁,要5秒后线程2执行完释放了锁 线程1已才能获取到锁继续添加元素
package examination;

import java.util.ArrayList;
import java.util.List;

/**
 * taobao examination
 * 实现一个容器,提供两个方法,add和size
 * 写两个线程,线程1添加10个元素到容器中,
 * 线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 *
 * 这里使用wait()和notify()方法实现,
 * 注意:wait()会释放锁,而nodify()不会释放锁。
 *
 * 该例中线程2必须比线程1先启动,才能保持监听状态
 * 因为必须先wait,才有nodify。
 *
 * 但是本程序的输出还是没有达到预期效果:
 * size=5的时候线程2没有继续往下执行,而是
 * 等到线程1执行完毕后才往下执行的,因为
 * nodify是不释放锁的,而wait被nodify后是需要
 * 重新持有锁的,但是现在这把锁被线程1持有着
 * 所以只能等到线程1离开同步代码块时,线程2
 * 才能继续往下执行。
 *
 * 解决方案:
 * 线程1在nodify后必须要释放锁,那么就得wait了,
 * 然后线程2被唤醒后也必须再nodify通知线程2。
 * @author xzq
 */
public class Examination04 {

    public static void main(String[] args) {
        final Box_4 box=new Box_4();
        final Object obj=new Object();

        new Thread("线程2"){

            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        System.out.println("线程2:开始执行");
                        if(box.size()!=5){
                            obj.wait();
                        }
                        System.out.println("线程2:执行完毕!");
                        //通知线程2我已经被唤醒了,你也别等着了
                        obj.notify();
                        /*
                         上面notify只是唤醒了线程1,但我并没有释放锁 
                         要5秒后我执行完释放了锁 线程1已才能获取到锁继续添加元素
                         */
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();



        new Thread("线程1"){

            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        System.out.println("线程1:开始执行");
                        for(int i=1;i<=10;i++){
                            box.add(new Object());
                            System.out.println("线程1:add第"+i+"个元素");
                            if(box.size()==5){
                                obj.notify();
                                //释放锁,让线程2得以执行。
                                obj.wait();
                            }
                            Thread.sleep(1_000);
                        }
                        System.out.println("线程1:执行完毕!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

}

/**
 * 加上volatile后使box的修改可以得到通知
 *
 * @author xzq
 */
class Box_4{

    private volatile List<Object> box=new ArrayList<>();

    public void add(Object element){
        box.add(element);
    }

    public int size(){
        return box.size();
    }

}

在这里插入图片描述
5、使用CyclicBarrier方法解决

     只有设置为2的时候才可以满足,设置3就不行
     CyclicBarrier 是篱栅的意思,要等两个线程都执行完才能进行下一步
     await()可以是线程进入barrier(障碍)状态,当两个线程都进入barrier状态时
     两个线程继续执行下面的内容 不需再等待

final CyclicBarrier barrier=new CyclicBarrier(2);
barrier.await();

package examination;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * taobao examination
 * 实现一个容器,提供两个方法,add和size
 * 写两个线程,线程1添加10个元素到容器中,
 * 线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 *
 * 这里使用CountDownLatch/CyclicBarrier/Semaphore
 * 都可以简便解决这个需求
 * @author xzq
 */
public class Examination05 {


    public static void main(String[] args) {
        final Box_5 box=new Box_5();
         /*
         只有设置为2的时候才可以满足,设置3就不行
         CyclicBarrier 是篱栅的意思,要等两个线程都执行完才能进行下一步
         await()可以是线程进入barrier(障碍)状态,当两个线程都进入barrier状态时
         两个线程继续执行下面的内容 不需再等待
         */
        final CyclicBarrier barrier=new CyclicBarrier(2);

        new Thread("线程2"){

            @Override
            public void run() {
                try {
                    System.out.println("线程2:开始执行");
                    if(box.size()!=5){
                        barrier.await();
                    }
                    System.out.println("线程2:执行完毕!");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }.start();



        new Thread("线程1"){

            @Override
            public void run() {
                try {
                    System.out.println("线程1:开始执行");
                    for(int i=1;i<=10;i++){
                        box.add(new Object());
                        System.out.println("线程1:add第"+i+"个元素");
                        if(box.size()==5){
                            barrier.await();
                        }
                        Thread.sleep(1_000);
                    }
                    System.out.println("线程1:执行完毕!");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

}

/**
 * 加上volatile后使box的修改可以得到通知
 *
 * @author xzq
 */
class Box_5{

    private volatile List<Object> box=new ArrayList<>();

    public void add(Object element){
        box.add(element);
    }

    public int size(){
        return box.size();
    }

}

在这里插入图片描述
6、使用Semaphore 信号灯方法

package examination;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 *  taobao examination
 * 实现一个容器,提供两个方法,add和size
 * 写两个线程,线程1添加10个元素到容器中,
 * 线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 *
 * 这里使用CountDownLatch/CyclicBarrier/Semaphore
 * 都可以简便解决这个需求
 * @author xzq
 */
public class Examination06 {


    public static void main(String[] args) {
        final Box_6 box=new Box_6();

        final Semaphore reachSema=new Semaphore(0);


//      final Semaphore addSema=new Semaphore(10);

        final Semaphore addSema=new Semaphore(1);


        new Thread("线程2"){

            @Override
            public void run() {
                try {
                    System.out.println("线程2:开始执行");
                    if(box.size()!=5){
                        reachSema.acquire();
                    }
                    System.out.println("线程2:执行完毕!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();




        new Thread("线程1"){

            @Override
            public void run() {
                try {
                    System.out.println("线程1:开始执行");
                    for(int i=1;i<=10;i++){
                        addSema.acquire();
                        box.add(new Object());
                        System.out.println("线程1:add第"+i+"个元素");
                        if(box.size()==5){
                            reachSema.release();
                        }
                        addSema.release();
                        Thread.sleep(1_000);
                    }
//                      addSema.release();//这里是创建10个信号灯的实现
                    System.out.println("线程1:执行完毕!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();

    }

}

/**
 * 加上volatile后使box的修改可以得到通知
 *
 * @author xzq
 */
class Box_6{

    private volatile List<Object> box=new ArrayList<>();

    public void add(Object element){
        box.add(element);
    }

    public int size(){
        return box.size();
    }

}


在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值