栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

并发编程-LockSupport和wait¬ify的区别

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

并发编程-LockSupport和wait&notify的区别

目录
    • LcokSupport
      • 定义
      • 常用的方法和基本原理了解
      • 应用
    • notify&wait
      • 定义
      • 应用示例
      • 基本原理
    • 两者之间的区别
    • 扩展1(如何使用wait¬ify实现一个阻塞队列)

LcokSupport 定义

LockSupport是一个在JUC包里的线程阻塞工具类,所有的方法都是静态方法,主要用途是让线程在任意位置阻塞,广泛应用在AQS和各种JUC的锁中

常用的方法和基本原理了解
public static void park(Object blocker); // 暂停当前线程
public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制
public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间
public static void park(); // 无期限暂停当前线程
public static void parkNanos(long nanos); // 暂停当前线程,不过有超时时间的限制
public static void parkUntil(long deadline); // 暂停当前线程,直到某个时间
public static void unpark(Thread thread); // 恢复当前线程

可以看到这里的方法主要就是park和unpark也就是阻塞和唤醒的作用
park指的就是停车
unpark指的就是放行
这里的blocker指的一般就是当前的线程对象,方便dump时分析问题,
我们可以尝试先在代码使用下这些方法

static class ParkThread implements Runnable{

        @Override
        public void run() {
            System.out.println( Thread.currentThread().getName() + "开始线程阻塞");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "结束线程阻塞");
        }
    }
    public static void main(String[] args) {
        ParkThread parkThread = new ParkThread();
        Thread t1=new Thread(parkThread);
        Thread t2=new Thread(parkThread);
        t1.start();
        LockSupport.unpark(t1);
        t2.start();
        LockSupport.unpark(t2);
    }

这样代码运行是没有问题,那么如果先执行unpark方法,后执行park方法会怎么样呢

static class ParkThread implements Runnable{

        @Override
        public void run() {
            System.out.println( Thread.currentThread().getName() + "开始线程阻塞");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "结束线程阻塞");
        }
    }
    public static void main(String[] args) {
        ParkThread parkThread = new ParkThread();
        Thread t1=new Thread(parkThread);
        Thread t2=new Thread(parkThread);
        t1.start();
        LockSupport.unpark(t1);
        t2.start();
        LockSupport.unpark(t2);
    }

运行后会发现,线程并没有阻塞,而是和之前一样执行成功了,这个就是和notify和wait最大的区别了,因为notify的唤醒顺序是不能颠倒的.

那么LockSupport是怎么实现这个功能的呢?这里是基本介绍,我就不和大家分析源码了,给大家描述一下流程:

源码里其实是通过一个_counter变量来控制的
_counter可以作为一个凭证
unpark方法的时候把_counter置为1
park方法判断如果_counter=1的话就不堵塞,并且把_counter设置为0,
换句话说当_counter=1的时候,调用park方法是不会阻塞的, 所以调用多次unpark方法后,调用一次park方法,不会堵塞
但是调用多次unpark方法后,调用多次park方法就会堵塞

应用

在之前分析AQS源码ReentrantLock加锁解锁源码详细解析的时候,我们看到过LockSupport的实际应用场景,其实在Condition和CountDownLatch等常用的并发类中用的也都是LockSupport

notify&wait 定义

notify和wait都是object的方法,一般被用作线程间的协作,也可以实现线程的等待和唤醒,功能和LockSupport较为类似

应用示例
private static Object obj = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(new WaitThread(), "wait-1").start();
        Thread.sleep(100);
        new Thread(new NotifyThread(), "notify-1").start();
    }

    static class WaitThread implements Runnable{

        @Override
        public void run() {
            synchronized (obj){
                System.out.println("start wait");
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("end wait");
            }

        }
    }
    static class NotifyThread implements Runnable{

        @Override
        public void run() {
            synchronized (obj){
                System.out.println("start notify");
                obj.notify();
                System.out.println("end notify");
            }
        }
    }

需要注意的是notify和wait必须在同步代码块中使用,否则就会出现IllegalMonitorStateException

start wait
start notify
Exception in thread "notify-1" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at com.example.skill.lock.NotifyTest$NotifyThread.run(NotifyTest.java:40)
	at java.lang.Thread.run(Thread.java:748)
基本原理

wait方法:

  • 把当前线程封装成一个node
  • 通过objectmonitor::addwaiter方法将node添加到_WaitSet列表中
  • 通过ObjectMonitor:exit方法释放当前的ObjectMonitor对象,这样其他竞争线程就可以获取该ObjectMonitor对象
  • 最终还是代调用了park方法挂起线程

notify方法则是随机唤醒等待池中的一个线程

两者之间的区别

相同点:

  • wait¬ift和LockSupport都可以用作线程之间的协作
  • 低层其实都是调用了park方法来挂起线程

不同点:

  • wait¬ify必须在同步代码块中使用,LockSupport则可以在任意场合使用
  • wait¬ify不能颠倒顺序使用,LockSupport可以颠倒park和unpark的顺序
  • wait¬ify不能唤醒指定的线程,但是LockSupport可以
  • notifyAll方法可以唤醒所有等待的线程,但是LockSupport只能唤醒单个线程
扩展1(如何使用wait¬ify实现一个阻塞队列)
 public static void main(String[] args) throws InterruptedException {
        NotifyBlockQueue notifyBlockQueue = new NotifyBlockQueue(5);
        for (int i = 0; i < 10; i++) {
            final int a = i;
            new Thread(() -> {
                try {
                    notifyBlockQueue.put(a);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    System.out.println("从队列取出" + notifyBlockQueue.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

        }

    }
    static class NotifyBlockQueue{
        //模拟队列
        private linkedList linkedList= new linkedList();
        //最大容量
        private int maxSize;

        public NotifyBlockQueue(int maxSize){
            this.maxSize = maxSize;
        }

        //队列是否满了
        public boolean isFull(){
            return linkedList.size() == maxSize;
        }

        //队列是否为空
        public boolean isEmpty(){
            return linkedList.isEmpty();
        }
        public synchronized void put(Object value) throws InterruptedException {
            //如果队列满了就一直等待
            while (isFull()){
                this.wait();
            }
            linkedList.add(value);
            System.out.println("开始往队列放入" + value);
            this.notifyAll();
        }
        public synchronized Object get() throws InterruptedException {
            while (isEmpty()){
                this.wait();
            }
            Object o = linkedList.removeFirst();
            this.notifyAll();
            return o;
        }
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/337001.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号