使用Java的同窗对Thread应该不陌生了,线程的建立和启动等这里就不讲了,这篇主要讲几个容易被忽视的方法以及线程状态迁移。java
首先咱们要明白这三个方法是定义在Object类中,他们起到的做用就是容许线程就资源的锁定状态进行通讯。这里所说的资源通常就是指的咱们常说的共享对象了,也就是说针对共享对象的锁定状态能够经过wait/notify/notifyAll来进行通讯。咱们先看下如何使用的,并对相应原理进行展开。编程
wait方法告诉调用线程放弃锁定并进入休眠状态,直到其余某个线程进入同一个监视器(monitor)并调用notify方法。wait方法在等待以前释放锁,并在wait方法返回以前从新获取锁。wait方法实际上和同步锁紧密集成,补充同步机制没法直接实现的功能。
须要注意到wait方法在jdk源码中是final而且是native的本地方法,咱们没法去覆盖此方法。
调用wait通常的方式以下:segmentfault
synchronized(lockObject) { while(!condition) { lockObject.wait(); } // 这里进行相应处理; }
注意这里使用while进行条件判断而没有使用if进行条件判断,缘由是这里有个很重要的点容易被忽视,下面来自官方的建议:安全
应该在循环中检查等待条件,缘由是处于等待状态的线程可能会收到错误的警报和伪唤醒,若是不在循环条件中等待,程序就会在没有知足结束条件的状况下退出。
notify方法唤醒了同一个对象上调用wait的线程。这里要注意notify并无放弃对资源的锁定,他告诉等待的线程能够唤醒,可是做用在notify上synchronized同步块完成以前,其实是不会放弃锁。所以,若是通知线程在同步块内,调用notify方法后,须要在进行10s的其余操做,那么等待的线程将会再至少等待10s。
notify通常的使用方式以下:并发
synchronized(lockObject) { // 肯定条件 lockObject.notify(); // 若是须要能够加任意代码 }
notifyAll会唤醒在同一个对象上调用wait方法的全部线程。在大多数状况下优先级最高的线程将被执行,可是也是没法彻底保证会是这样。其余的与notify相同。app
下面的代码示例实现了队列空和满时线程阻塞已经非空非满时的通知:ide
生产者:测试
class Producer implements Runnable { private final List<Integer> taskQueue; private final int MAX_CAPACITY; public Producer(List<Integer> sharedQueue, int size) { this.taskQueue = sharedQueue; this.MAX_CAPACITY = size; } @Override public void run() { int counter = 0; while (true) { try { produce(counter++); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private void produce(int i) throws InterruptedException { synchronized (taskQueue) { while (taskQueue.size() == MAX_CAPACITY) { System.out.println("队列已满,线程" + Thread.currentThread().getName() + "进入等待,队列长度:" + taskQueue.size()); taskQueue.wait(); } Thread.sleep(1000); taskQueue.add(i); System.out.println("生产:" + i); taskQueue.notifyAll(); } } }
消费者:this
class Consumer implements Runnable { private final List<Integer> taskQueue; public Consumer(List<Integer> sharedQueue) { this.taskQueue = sharedQueue; } @Override public void run() { while (true) { try { consume(); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private void consume() throws InterruptedException { synchronized (taskQueue) { while (taskQueue.isEmpty()) { System.out.println("队列已空,线程" + Thread.currentThread().getName() + "进入等待,队列长度:" + taskQueue.size()); taskQueue.wait(); } Thread.sleep(1000); int i = (Integer) taskQueue.remove(0); System.out.println("消费:" + i); taskQueue.notifyAll(); } } }
测试代码:spa
public class ProducerConsumerExampleWithWaitAndNotify { public static void main(String[] args) { List<Integer> taskQueue = new ArrayList<>(); int MAX_CAPACITY = 5; Thread tProducer = new Thread(new Producer(taskQueue, MAX_CAPACITY), "Producer"); Thread tConsumer = new Thread(new Consumer(taskQueue), "Consumer"); tProducer.start(); tConsumer.start(); } }
部分输出以下:
生产:0 生产:1 生产:2 生产:3 生产:4 队列已满,线程Producer进入等待,队列长度:5 消费:0 消费:1 消费:2 消费:3 消费:4 队列已空,线程Consumer进入等待,队列长度:0
从字面意思理解yield能够是谦让、放弃、屈服、投降的意思。一个要“谦让”的线程实际上是在告诉虚拟机他愿意让其余线程安排到他的前面,这代表他没有说明重要的事情要作了。注意了这只是个提示,并不能保证能起到任何效果。
yield在Thread.java中定义以下:
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield();
从这里面咱们总结出一些重点(有关线程状态后面会讲到):
yield使用例子:
public class YieldExample { public static void main(String[] args) { Thread producer = new Producer(); Thread consumer = new Consumer(); producer.setPriority(Thread.MIN_PRIORITY); // 最低优先级 consumer.setPriority(Thread.MAX_PRIORITY); // 最高优先级 producer.start(); consumer.start(); } } class Producer extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("生产者 : 生产 " + i); Thread.yield(); } } } class Consumer extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("消费者 : 消费 " + i); Thread.yield(); } } }
当注释两个“Thread.yield();”时输出:
消费者 : 消费 0 消费者 : 消费 1 消费者 : 消费 2 消费者 : 消费 3 消费者 : 消费 4 生产者 : 生产 0 生产者 : 生产 1 生产者 : 生产 2 生产者 : 生产 3 生产者 : 生产 4
当不注释两个“Thread.yield();”时输出:
生产者 : 生产 0 消费者 : 消费 0 生产者 : 生产 1 消费者 : 消费 1 生产者 : 生产 2 消费者 : 消费 2 生产者 : 生产 3 消费者 : 消费 3 生产者 : 生产 4 消费者 : 消费 4
join方法用于将线程当前执行点链接到另外一个线程的执行结束,这样这个线程就不会开始运行直到另外一个线程结束。在Thread实例上调用join,则当前运行的线程将会阻塞,直到这个Thread实例完成执行。
简要摘抄Thread.java源码中join的定义:
// Waits for this thread to die. public final void join() throws InterruptedException
join还有能够传入时间参数的重载方法,这个能够时join的效果在特定时间后无效。当达到超时时间时,主线程和taskThread是一样可能的执行者候选。可是join和sleep同样,依赖于OS进行计时,不该该假定恰好等待指定的时间。
join和sleep同样也经过InterruptedException来响应中断。
join使用示例:
public class JoinExample { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Runnable() { public void run() { System.out.println("第一个任务启动"); System.out.println("睡眠2s"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第一个任务完成"); } }); Thread t1 = new Thread(new Runnable() { public void run() { System.out.println("第二个任务完成"); } }); t.start(); t.join(); t1.start(); } }
输出结果:
第一个任务启动 睡眠2s 第一个任务完成 第二个任务完成
join在Thread.java中有三个重载方法:
public final void join() throws InterruptedException public final synchronized void join(long millis) throws InterruptedException public final synchronized void join(long millis, int nanos) throws InterruptedException
查看源码能够得知最终的实现核心部分都在join(long millis)中,咱们来分析下这个方法源码:
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
首先能够看到这个方法是使用synchronized修饰的同步方法,从这个方法的源码能够看出join的核心就是使用wait来实现的,而外部条件就是isAlive(),能够判定,在非isAlive()时会进行notify。
在Thread.java的源代码中就体现出了六种状态:
/** * A thread state. A thread can be in one of the following states: * <ul> * <li>{@link #NEW}<br> * A thread that has not yet started is in this state. * </li> * <li>{@link #RUNNABLE}<br> * A thread executing in the Java virtual machine is in this state. * </li> * <li>{@link #BLOCKED}<br> * A thread that is blocked waiting for a monitor lock * is in this state. * </li> * <li>{@link #WAITING}<br> * A thread that is waiting indefinitely for another thread to * perform a particular action is in this state. * </li> * <li>{@link #TIMED_WAITING}<br> * A thread that is waiting for another thread to perform an action * for up to a specified waiting time is in this state. * </li> * <li>{@link #TERMINATED}<br> * A thread that has exited is in this state. * </li> * </ul> * * <p> * A thread can be in only one state at a given point in time. * These states are virtual machine states which do not reflect * any operating system thread states. * * @since 1.5 * @see #getState */ public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
通常咱们用以下图来表示状态迁移,注意相关方法。(注意:其中RUNNING和READY是没法直接获取的状态。)