线程通信

**

通信的方式

**
要想实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程执行的结果等等。
涉及到线程之间相互通信,分为下面四类:
1) 文件共享
2) 网络共享
3) 变量共享
4) jdk提供的线程协调API
细分为:suspend/resume、wait/notify、park/unpark

线程协作 - JDK API
JDK中对于需要多线程协作完成某一任务的场景,提供了对应API支持。
对线程协作的典型场景是:生产者-消费者模型。(线程阻塞、线程唤醒)

API -
被弃用的suspend和resume (不会对锁进行释放锁 顺序问题)
作用:调用suspend挂起目标线程,通过resume可以恢复线程执行
被弃用的主要原因,容易写出死锁的代码。
所以用wait/notify、park/unpark机制对它进行替代

1、suspend挂起之后并不会释放锁,故此容易写出死锁
2、先执行suspend,再执行resume。反之则死锁
public class Test01 {
        /**
         * 线程协作通信的方式:
         *  suspend/resume
         */
        /**包子店*/
        public static Object baozidian =null;

        /*正常的supend/resume */
        public void suspendReumeTest() throws InterruptedException {
            //启动线程
            Thread consumerThread =new Thread(
                    new Runnable() {

                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            for(;;) {
                                if (baozidian == null) {//如果没有包子则进入等待
                                    System.out.println("1、进入等待");
                                    Thread.currentThread().suspend();
                                } else {
                                    System.out.println("2、买到包子回家");
                                    return;
                                }
                            }
                        }

                    });
            consumerThread.start();
            //3秒种之后生产一个包子
            Thread.sleep(3000L);
            baozidian=new Object();
            consumerThread.resume();
            System.out.println("3、通知消费者");
        }
        /** 死锁的suspend/resume。 suspend并不会像wait一样释放锁,故此容易写出死锁代码 */
        public void suspendResumeDeadLockTest() throws Exception {
            // 启动线程
            Thread consumerThread = new Thread(() -> {
                for(;;) {
                    if (baozidian == null) { // 如果没包子,则进入等待
                        System.out.println("1、进入等待");
                        // 当前线程拿到锁,然后挂起
                        synchronized (this) {
                            Thread.currentThread().suspend();
                        }
                    } else {
                        System.out.println("2、买到包子,回家");
                    }
                }
            });
            consumerThread.start();
            // 3秒之后,生产一个包子
            Thread.sleep(3000L);
            baozidian = new Object();
            // 争取到锁以后,再恢复consumerThread
            synchronized (this) {
                consumerThread.resume();
            }
            System.out.println("3、通知消费者");
        }

        //导致程序永久挂起的suspend/resume
        public void suspendResumeDeadLockTest2() throws InterruptedException {
            //启动线程
            Thread consumerThread =new Thread(() -> {
                // TODO Auto-generated method stub
                for(;;) {
                    if (baozidian == null) {
                        System.out.println("1、没包子,进入等待");
                        try {
                            Thread.sleep(5000L);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        //这里挂起执行在resume后面
                        Thread.currentThread().suspend();
                    } else {
                        System.out.println("2、买到包子,回家");
                    }
                }
            });
            consumerThread.start();
            //3秒之后,生产一个包子
            Thread.sleep(3000L);
            baozidian =new Object();
            consumerThread.resume();
            System.out.println("3、通知消费者");
            consumerThread.join();
        }

        public static void main(String[] args) throws Exception {
            //对调用顺序有要求,也要开发自己注意锁的释放,这个被启用的API,容易死锁,也容易导致永久挂起
            new Test01().suspendReumeTest();
            //new Test01().suspendResumeDeadLockTest();
            //new Test01().suspendResumeDeadLockTest2();
        }

}

wait/notify机制 (顺序问题)
这些方法只能由同一对象锁的持有者线程调用,也就是卸载同步块里面,否则会抛出
IllegalMonitorStateException异常。

wait方法导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。。
notify/notifyAll 方法唤醒一个或所有正在等待这个对象锁的线程。

注意:虽然wait会自动解锁,但是对 顺序有要求,如果在notify被调用之后,才开始wait
方法的调用,线程会永远处于waiting状态。

public class Test01 {
    /**
     * 线程协作通信的方式:
     *  suspend/resume
     */
    /**
     * 包子店
     */
    public static Object baozidian = null;

    /**
     * 正常的wait/notify
     */
    public void waitNotifyTest() throws Exception {
        // 启动线程
        new Thread(() -> {
            if (baozidian == null) { // 如果没包子,则进入等待
                synchronized (this) {
                    try {
                        System.out.println("1、进入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("2、买到包子,回家");
        }).start();
        // 3秒之后,生产一个包子
        Thread.sleep(3000L);
        baozidian = new Object();
        synchronized (this) {
            this.notifyAll();
            System.out.println("3、通知消费者");
        }
    }

    /**
     * 会导致程序永久等待的wait/notify
     */
    public void waitNotifyDeadLockTest() throws Exception {
        //启动线程
        new Thread(() -> {
            if (baozidian == null) {//如果没有包子,则进入等待
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized (this) {
                    try {
                        System.out.println("1、进入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println("2、买到包子,回家");
            }
        }).start();
        //3秒种之后,生产一个包子
        Thread.sleep(3000L);
        baozidian = new Object();
        synchronized (this) {
            this.notifyAll();
            System.out.println("3、通知消费者");

        }
    }

    public static void main(String[] args) throws Exception {
        //对调用顺序有要求,也要开发自己注意锁的释放,这个被启用的API,容易死锁,也容易导致永久挂起
        new Test01().waitNotifyTest();
        //new Test01().waitNotifyDeadLockTest();
    }

}

park/unpark机制 令牌模式 (不会对锁进行释放锁 同步锁)
线程调用park则等待 ‘许可’,unpark方法为指定线程提供 ‘许可(permit)’。

不要求park和unpark方法的调用顺序。
import java.util.concurrent.locks.LockSupport;

public class Test01 {
    /**
     * 线程协作通信的方式:
     *  suspend/resume
     */
    /**
     * 包子店
     */
    public static Object baozidian = null;

    /**
     * 正常的wait/notify
     */
    /**
     * 正常的park/unpark
     *
     * @throws InterruptedException
     */
    public void parkUnparkTest() throws InterruptedException {
        //启动线程
        Thread consumerThread = new Thread(() -> {
            if (baozidian == null) {
                System.out.println("1|进入等待");
                LockSupport.park();
            }
            System.out.println("2、买到包子,回家");
        });
        consumerThread.start();
        //3秒之后,生产一个包子
        Thread.sleep(3000L);
        baozidian = new Object();
        LockSupport.unpark(consumerThread);
        System.out.println("3、通知消费者");
    }

    /**
     * 死锁的park/unpark
     */
    public void parkUnparkDeadLockTest() throws Exception {
        //启动线程
        Thread consumerThread = new Thread(() -> {
            if (baozidian == null) {//如果没有包子 进入等待
                System.out.println("1、进入等待");
                //当前线程拿到锁,然后挂起
                synchronized (this) {
                    LockSupport.park();
                }
            }
            System.out.println("2、买到包子,回家");
        });
        consumerThread.start();
        //3秒之后,生产一个包子
        Thread.sleep(3000L);
        baozidian = new Object();
        //争取到锁之后,再回复consumerThread
        synchronized (this) {
            LockSupport.unpark(consumerThread);
        }
        System.out.println("3、通知消费者");
    }

    public static void main(String[] args) throws Exception {
        //对调用顺序有要求,也要开发自己注意锁的释放,这个被启用的API,容易死锁,也容易导致永久挂起
        new Test01().parkUnparkTest();
        //new Test01().parkUnparkDeadLockTest();
    }

}

伪唤醒
官方建议应该在循环中检查等待条件,原因是处于等待状态的线程坑你
会收到 错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足
结束条件的情况下退出。

伪唤醒是指线程并非因为notify、notifyall、unpark等API调用而唤醒,是更底层原因导致的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值