方法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后的方法。
新建(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)状态。 复制代码
本节代码 GitHub
在很多情况,主线程创建并启动子线程,如果子线程中进行大量的耗时运算,主线程往往将遭遇子线程结束之前结束。如果主线程要等待子线程执行完成之后在结束,就要使用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执行完 复制代码
方法join(long)的功能是在内部使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。二sleep(long)不会释放锁。
变量值共享可以使用public static变量的形式,所有线程都使用同一个public static变量,如果想实现每个线程都有自己的共享变量可以使用ThreadLocal来解决。
ThreadLocal的相关方法:
public class ThreadLocalTeat { public static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) throws InterruptedException{ int count = 30; String name = "Thread-"; for (int i=0; i<count; 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可以在子线程中获取父线程继承下来的值。
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中的值进行更改,那么子线程取到的还是旧值。
欢迎关注公众号: