java 线程等待队列_Java多线程学习(五)——等待通知机制

等待通知机制的实现

方法wait()的作用是使当前线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程放到“预执行队列”,并在wait()所在的代码处停止执行,直到接到通知或中断为止。只能在同步方法或同步快中使用wait()方法,执行wait()后,当前线程释放锁。

方法notify()也要在同步方法或同步快中调用,在调用前也必须获得该对象的的对象级别锁。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机选出一个wait状态的线程,对其发出notify通知,使他等待获取对象锁。

在执行notify()后当前线程不会马上释放锁,会在线程退出synchronized代码块才会释放锁,呈wait状态的线程才可以获取锁。当第一个获取对象锁的wait线程运行结束释放锁后,如果该对象没有再次notify,其他wait状态的线程依然会阻塞wait状态,直到这个对象发出notify或notifyAll。

public class MyWait {

private final Object lock;

MyWait(Object lock){

this.lock=lock;

}

public void waitTest(){

try {

synchronized (lock){

System.out.println("开始 wait time = " + System.currentTimeMillis());

lock.wait();

System.out.println("结束 wait time = " + System.currentTimeMillis());

}

}catch (InterruptedException e){

e.printStackTrace();

}

}

}

public class MyNotify {

private final Object lock;

MyNotify(Object lock){

this.lock=lock;

}

public void notifyTest(){

synchronized (lock){

System.out.println("开始 notify time = " + System.currentTimeMillis());

lock.notify();

System.out.println("结束 notify time = " + System.currentTimeMillis());

}

}

}

public class Main {

public static void main(String[] args){

try {

Object lock = new Object();

MyWait myWait = new MyWait(lock);

new Thread(() -> myWait.waitTest()).start();

Thread.sleep(3000);

MyNotify myNotify = new MyNotify(lock);

new Thread(() -> myNotify.notifyTest()).start();

}catch (InterruptedException e){

e.printStackTrace();

}

}

}

开始 wait time = 1552812964325

开始 notify time = 1552812967328

结束 notify time = 1552812967328

结束 wait time = 1552812967328

从输出内容可以看出3秒后执行notify方法,并在notify方法执行结束后才执行wait后的方法。

相关方法wait() :使调用该方法的线程释放共享资源锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。

wait(long):超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。

notify():随机唤醒等待队列中等待同一共享资源的 “一个线程”,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知“一个线程”。

notifyAll():使所有正在等待队列中等待同一共享资源的 “全部线程” 退出等待队列,进入可运行状态。此时,优先级最高的那个线程最先执行,但也有可能是随机执行,这取决于JVM虚拟机的实现。

线程的基本状态

新建(new):新创建了一个线程对象。

可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取cpu的使用权。

运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。

阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有 机会再次获得cpu timeslice转到运行(running)状态。阻塞的情况分三种:

(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放 入等待队列(waitting queue)中。

(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。

(三). **其他阻塞**: 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。死亡(dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

本节代码lgsxiaosen/notes-code​github.comd9a54148e43a764f202e6f9b90a6dcc2.png

方法join的使用

在很多情况,主线程创建并启动子线程,如果子线程中进行大量的耗时运算,主线程往往将遭遇子线程结束之前结束。如果主线程要等待子线程执行完成之后在结束,就要使用join()方法,join()作用是等待线程对象销毁。

Thread类除了提供join()方法之外,还提供了join(long millis)、join(long millis, int nanos)两个具有超时特性的方法。这两个超时方法表示,如果线程thread在指定的超时时间没有终止,那么将会从该超时方法中返回。

public class Main {

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

Thread thread = new Thread(() -> {

try {

System.out.println(Thread.currentThread().getName()+"正在执行");

Thread.sleep(1000);

}catch (InterruptedException e){

e.printStackTrace();

}

}, "线程1");

thread.start();

thread.join();

System.out.println("等待"+thread.getName()+"执行完");

}

}

// 输出线程1正在执行

等待线程1执行完

jain(long)与sleep(long)的区别

方法join(long)的功能是在内部使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。二sleep(long)不会释放锁。

ThreadLocal的使用

变量值共享可以使用public static变量的形式,所有线程都使用同一个public static变量,如果想实现每个线程都有自己的共享变量可以使用ThreadLocal来解决。

ThreadLocal的相关方法:get():返回当前线程的此线程局部变量的副本中的值。

set(T value): 将当前线程的此线程局部变量的副本设置为指定的值。

remove():删除此线程局部变量的当前线程的值。

initialValue(): 返回此线程局部变量的当前线程的“初始值”。

线程变量间的隔离性

public class ThreadLocalTeat {

public static ThreadLocal threadLocal = new ThreadLocal<>();

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

int count = 30;

String name = "Thread-";

for (int i=0; i

Thread thread = new Thread(() -> {

threadLocal.set(Thread.currentThread().getName());

System.out.println(threadLocal.get());

}, name+i);

thread.start();

}

Thread.sleep(20000);

}

}

// 输出Thread-0

Thread-4

Thread-3

Thread-6

Thread-2

Thread-1

Thread-7

。。。

InheritableThreadLocal的使用

使用类InheritableThreadLocal可以在子线程中获取父线程继承下来的值。

public class InheritableThreadLocalTest extends InheritableThreadLocal {

@Override

protected Object childValue(Object parentValue) {

return super.childValue(parentValue);

}

@Override

protected Object initialValue() {

return System.currentTimeMillis();

}

}

* @date 2019/6/18 8:28

* @description

*/

public class InheritableTeat {

static public class Inner{

public static InheritableThreadLocalTest threadLocalTest = new InheritableThreadLocalTest();

}

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

for (int i = 0; i<3; i++){

System.out.println("在main线程中获取值:"+ Inner.threadLocalTest.get());

}

for (int i=0; i<3; i++){

new Thread(() -> {

System.out.println("在"+Thread.currentThread().getName()+"中获取值:"+ Inner.threadLocalTest.get());

}, "Thread-"+i).start();

}

Thread.sleep(1000);

}

}

// 输出在main线程中获取值:1560818029616

在main线程中获取值:1560818029616

在main线程中获取值:1560818029616

在Thread-1中获取值:1560818029616

在Thread-2中获取值:1560818029616

在Thread-0中获取值:1560818029616

在使用InheritableThreadLocal类需要注意的一点是:如果子线程在取得值的同时,主线程将InheritableThreadLocal中的值进行更改,那么子线程取到的还是旧值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值