关于java:JAVA并发编程线程等待唤醒机制与LockSupport

10次阅读

共计 2883 个字符,预计需要花费 8 分钟才能阅读完成。

1. 线程期待唤醒机制概述

2.Object 类中的 wait 和 notify 办法实现线程期待和唤醒

3.Condition 接口中的 await 后 signal 办法实现线程的期待和唤醒

4.Object 和 Condition 应用的限度条件

5.LockSupport 类简介

6.LockSupport 类中的 park 期待和 unpark 唤醒

7. 总结

1. 线程期待唤醒机制概述

通过后面两篇博客:
JAVA 并发编程——Synchronized 与 Lock 的区别以及 Lock 的应用
JAVA 并发编程——生产者与消费者模式(传统版 & 阻塞队列版)

咱们初步理解了线程的期待和唤醒机制,并写了几个 demo 来应征了一个线程来唤醒另外一个线程,然而这两种写法并不是十分完满,具体哪里不完满,以及批改计划,都在一下的博客中进行了解释。

2.Object 类中的 wait 和 notify 办法实现线程期待和唤醒

public class LockDemo
{public static void main(String[] args)//main 办法,主线程所有程序入口
{Object objectLock = new Object(); // 同一把锁,相似资源类

        new Thread(() -> {synchronized (objectLock) {
           try {objectLock.wait();
                } catch (InterruptedException e) {e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"\t"+"被唤醒了");
        },"t1").start();

           // 暂停几秒钟
           Thread.sleep(3000);

           new Thread(() -> {synchronized (objectLock) {objectLock.notify();
            }
           },"t2").start();}
 }

缺点:
下面的代码尽管可能让一个线程唤醒另外一个线程,然而有着这样的一个缺点:
1)将 notify 放在 wait 办法后面,那么线程 t1 就无奈唤醒
2)wait 和 notify 办法必须要在同步块或者办法外面,且成对呈现应用

3.Condition 接口中的 await 后 signal 办法实现线程的期待和唤醒

public class LockSupportDemo2
{public static void main(String[] args)
    {Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(() -> {lock.lock();
        try
        {System.out.println(Thread.currentThread().getName()+"\t"+"start");
              condition.await();
              System.out.println(Thread.currentThread().getName()+"\t"+"被唤醒");
        } catch (InterruptedException e) {e.printStackTrace();
            } finally {lock.unlock();
            }
        },"t1").start();

// 暂停几秒钟线程
        try {TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) {e.printStackTrace(); }

        new Thread(() -> {lock.lock();
        try
        {condition.signal();
        } catch (Exception e) {e.printStackTrace();
            } finally {lock.unlock();
            }
         System.out.println(Thread.currentThread().getName()+"\t"+"告诉了");
        },"t2").start();}
}

缺点:
1) await()办法肯定要在 signal()办法之前
2) Condtion 中的线程期待和唤醒办法之前,须要先获取锁

4.Object 和 Condition 应用的限度条件

从下面能够看出
1) 线程先要取得并持有锁,必须在锁块 (synchronized 或 lock) 中
2) 必须要先期待后唤醒,线程才可能被唤醒

5.LockSupport 类简介

咱们先看看 api 怎么介绍 LockSupport 类的:

LockSupport 是用来创立锁和其余同步类的根本线程阻塞原语.

这里咱们可能看不懂,咱们持续看上面的源码正文:

根本意思就是说:

LockSupport 类应用了一种名为 Permit(许可)的概念来做到阻塞和唤醒线程的性能, 每个线程都有一个许可 (permit)
如果调用 park()办法,而且许可(permit 为 1)那么线程就会继续执行,否则线程就会立即阻塞。

permit 只有两个值 1 和零,默认是零。能够把许可看成是一种 (0,1) 信号量(Semaphore),但与 Semaphore 不同的是,许可的累加下限是 1。

6.LockSupport 类中的 park 期待和 unpark 唤醒
咱们先来看一下文档里的 park()和 unpark()办法:

park() /park(Object blocker) :
阻塞以后线程 / 阻塞传入的具体线程

unpark(Thread thread)
唤醒处于阻塞状态的指定线程


public class LockSupportDemo
{public static void main(String[] args)
    {
// 失常应用 + 不须要锁块
Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName()+""+"1111111111111");
    LockSupport.park();
    System.out.println(Thread.currentThread().getName()+""+"2222222222222------end 被唤醒 ");
},"t1");
t1.start();

// 暂停几秒钟线程
try {TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }

LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName()+"-----LockSupport.unparrk() invoked over");

}
}

长处:
1)谬误的先唤醒后期待,LockSupport 照样反对
2)无需无锁块要求

毛病:
1)因为标记位 1,所以只能唤醒一次线程。间断 park() unpark()没有成果。

** 7. 总结

明天咱们次要学习了以前的线程期待唤醒机制的毛病与改良办法 –LockSupport,LockSupport 其实是为了浏览当前的 aqs 源码而进行的一次前置常识,它很好地改良了后面几种唤醒机制的缺点。

正文完
 0