首页 > 编程语言 >JUC并发编程基础篇第六章之LockSupport[notify,signal之外的另一种唤醒方式]

JUC并发编程基础篇第六章之LockSupport[notify,signal之外的另一种唤醒方式]

时间:2023-04-07 15:45:10浏览次数:42  
标签:System JUC java Thread signal LockSupport t1 println out

目录

1、LockSupport有什么用

一般情况下,我们们有如下3种办法去唤醒一个线程

  1. 使用object方法的wait()方法,让线程等待;使用object的notify()方法进行唤醒
  2. 使用juc包中的condition的await()方法让线程等待,使用signal()方法唤醒线程
  3. LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

接下来将进行一一的讲解

2、使用wait和notify唤醒一个线程

2.1、正常情况

public class InterruputDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        new Thread(() -> {
            synchronized (o) {
                System.out.println(Thread.currentThread().getName() + "----coming in");
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "好的,多谢通知 ,我不等待了,");
            }
        }, "t1").start();

        Thread.sleep(1000);

        new Thread(() -> {
            synchronized (o) {
                o.notify();
                System.out.println("解锁了,不用等待了");
            }
        },"t2").start();
    }
}

2.2、异常情况2 ,这里去掉了 synchronized (o) {} 代码块

public class InterruputDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "----coming in");
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "好的,多谢通知 ,我不等待了,");
        }, "t1").start();

        Thread.sleep(1000);

        new Thread(() -> {
                o.notify();
                System.out.println("解锁了,不用等待了");
        },"t2").start();
    }
}

会出现如下的错误

t1----coming in
Exception in thread "t1" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at com.test.InterruputDemo1.lambda$main$0(InterruputDemo1.java:10)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "t2" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at com.test.InterruputDemo1.lambda$main$1(InterruputDemo1.java:21)
	at java.lang.Thread.run(Thread.java:748)

结论: wait和notify 必须要配合synchronized

2.3、异常情况3 先notify再wait

 public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "----coming in");
                try {
                    synchronized (o) {
                        o.wait();
                    }
                    System.out.println(Thread.currentThread().getName() + "好的,多谢通知 ,我不等待了,");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }, "t1").start();

        Thread.sleep(1000);

        new Thread(() -> {
            synchronized (o) {
                o.notify();
                System.out.println("解锁了,不用等待了");
            }
        },"t2").start();
    }

最后输出的结果是

解锁了,不用等待了
t1----coming in
xxxxx  程序没有停止 ,仍在执行!!!!

总结: wait和notify必须放到同步代码块和方法里面,且成对出现, 先wait后notify才行

3、使用await和signal唤醒一个线程

3.1、正常情况

 public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(()->{
            lock.lock();
            System.out.println("我等待被唤醒");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("被唤醒");
        },"t1").start();

        Thread.sleep(1000);

        new Thread(()->{
            lock.lock();
            condition.signal();
            System.out.println("你被释放了");
            lock.unlock();
        },"t2").start();
    }

3.2、异常情况: 如果去除锁块

    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(()->{
//            lock.lock();
            System.out.println("我等待被唤醒");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
//                lock.unlock();
            }
            System.out.println("被唤醒");
        },"t1").start();

        Thread.sleep(1000);

        new Thread(()->{
//            lock.lock();
            condition.signal();
            System.out.println("你被释放了");
//            lock.unlock();
        },"t2").start();
    }

输出结果为

我等待被唤醒
Exception in thread "t1" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
	at com.test.InterruputDemo1.lambda$main$0(InterruputDemo1.java:17)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "t2" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
	at com.test.InterruputDemo1.lambda$main$1(InterruputDemo1.java:30)
	at java.lang.Thread.run(Thread.java:748)

3.3、异常情况: 先执行signal后await

public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.lock();
            System.out.println("我等待被唤醒");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("被唤醒");
        },"t1").start();

        Thread.sleep(1000);

        new Thread(()->{
            lock.lock();
            condition.signal();
            System.out.println("你被释放了");
            lock.unlock();
        },"t2").start();
    }

输出结果

你被释放了
我等待被唤醒
xxxxx  程序没有停止 ,仍在执行!!!!

总结: Condition中的线程等待和唤醒都需要先获取锁,一定要先await,然后signal,顺序不能反

4、LockSupport的park等待和unpark唤醒

LockSupport类使用了一种名叫Permit的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可,但是与Semaphore不同,许可的累加上限是1

4.1、正常代码

  public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "---->com in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "---->被唤醒了");
        }, "t1");
        t1.start();

        Thread.sleep(1000);

        new Thread(()->{
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"---->我来唤醒你");
        },"t2").start();
    }

输出结果

t1---->com in
t2---->我来唤醒你
t1---->被唤醒了

4.2、先执行unPark 后执行park

public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "---->com in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "---->被唤醒了");
        }, "t1");
        t1.start();

        new Thread(()->{
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"---->我来唤醒你");
        },"t2").start();
    }

输出结果

t2---->我来唤醒你
t1---->com in
t1---->被唤醒了

重要说明:LockSupportL的许可累加是1

public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "---->com in");
            LockSupport.park();
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "---->被唤醒了");
        }, "t1");
        t1.start();

        new Thread(()->{
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"---->我来唤醒你");
        },"t2").start();
    }

输出结果

t2---->我来唤醒你
t1---->com in
xxxx 程序没有停止运行!!!

虽然unpark是一个生成一个许可证,但是他的上限只有1,只能通过1park,后面就无法通过了;

5、面试题

5.1、问题1: 为什么LockSupport可以突破wait和notify的原有的调用顺序

因为unpark获得了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,故不会阻塞。先发放了凭证后续可以畅通无阻。

5.2、问题2: 为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?

因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证,证不够,不能放行。

标签:System,JUC,java,Thread,signal,LockSupport,t1,println,out
From: https://www.cnblogs.com/itcastwzp/p/17296388.html

相关文章

  • JUC并发编程基础篇第五章之线程中断状态[你理解的线程中断,只会Thread.interrupted()
    目录1、什么是线程的中断机制2、isterruptinterruptedisInterrupted区别3、面试题3.1、如何停止中断运行中的线程3.2、当前线程的中断标识符为true,是不是线程就立马停止了3.3、如果线程处于被阻塞的状态(列入sleep,wait,join等状态),在别的线程调用当前线程的interrupt()方法,会发生......
  • 【Java 并发】【九】【AQS】【一】什么是AQS?为什么说它是JUC基础框架?
    1 前言这节我们来开始看AQS,这个东西可以说是搞Java的都知道的,本节会介绍一下AQS以及它提供的基本机制,后面再对AQS提供的每一个机制一个个深入的剖析。2  什么是AQS?(同步器基础框架)AQS叫做抽象队列同步器(AbstractQueuedSynchronizer),它是一个实现了同步器功能的基础框架,其......
  • 并发编程——JUC并发大厂面试问题
    摘要现如今,不管是应届毕业生还是工作了三五年之内的工程师,在面试招聘的时候JUC并发编程的是必须掌握的一个技能,否者你将会被面试官玩弄。本博文将整理有关于的JUC的大厂面试问题和答案。帮助大家在面试过程中能够回答面试官问题的一二。同时本人也总结相关的面试问题的在相关文档中......
  • 【Java 并发】【八】【Atomic】【一】JUC下的Atomic原子类体系概览
    1 前言这节我们就开始看看Atomic原子类系列,JUC包下提供的原子类底层的实现原理基本都是差不多的,都是基于volatile和CAS操作来保证线程安全的,我们后续会着重分析几个类。2  概览我们看下JUC下边都有哪些原子类:看上面的图形,我们使用红色圈中的那些,就是我们要着重讨论的,一共......
  • SignalR服务端及客户端实现
    服务器端:引用nuget:1、Microsoft.AspNet.SignalR.SelfHost2、Microsoft.Owin.CorsinternalclassProgram{staticvoidMain(string[]args){stringuri="http://localhost:8081";using(WebApp.Start(uri))......
  • 【打怪升级】【juc】关于LockSupport
    通过juc下LockSupport,可以达到阻塞和唤醒线程的操作 LockSupportLockSupport是juc下一个线程阻塞唤醒的工具类。它的主要方法有:每个使用LockSupport的线程,会有一个许可;调用park会立即返回,否则会被阻塞。如果许可不可用,则可以调用unpark供其可使用,......
  • postgresql signal 5生成core分析
    postgresqlsignal5生成core,如下:[zjh@hs-10-20-30-193data]$gdblightdbcore.49666GNUgdb(GDB)RedHatEnterpriseLinux7.6.1-120.el7Copyright(C)2013Free......
  • JUC——什么是线程死锁?如何避免死锁?
      下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况(代码来源于《并发编程之美》):publicclassDeadLockDemo{privatestaticObjectresource1=......
  • JUC 常用 4 大并发工具类 CountDownLatch、CyclicBarrier、Semaphore、ExChanger
    文章目录​​什么是JUC?​​​​4大常用并发工具类​​​​CountDownLatch​​​​CyclicBarrier​​​​Semaphore​​​​Exchanger​​什么是JUC?JUC就是java.util.concu......
  • 使用ASP.NET CORE SignalR实现APP扫描登录
    使用ASP.NETCORESignalR实现APP扫描登录 使用signalr实现APP扫码登录1.背景介绍在移动化时代,web开发很多时候都会带着移动端开发,这个时候为了减少重复输入账号密......