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源码而进行的一次前置常识,它很好地改良了后面几种唤醒机制的缺点。
发表回复