从这篇文章开始,我们正式进入Java并发与锁的世界。
何为并发?何为锁?在Java中一般都是以线程为单位执行任务的,当多个线程在执行任务过程中在同一时间对某个共享资源进行访问时,我们称这种现象为并发,而为了保证多个线程在并发获取这个共享资源的时候不会出现状态不一致的问题或者相互影响,需要对这个共享资源进行限制,只有通过这个限制的线程才能获取到这个共享资源的控制和操作权,而锁就是对资源的限制,得到了锁才能对锁包装的资源进行访问。
对锁的竞争牵涉到线程的状态切换,因此,在这里,我们首先需要知道在Java中线程都有哪些状态,这些状态之间又是怎么切换的。
我们首先来看下在JDK1.7中对于线程状态的定义,可以在java.lang.Thread类中看到,这个类中包含了一个内部枚举类State,定义了线程的一些状态,具体如下:
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;
}
从这个枚举中,我们可以看到分为NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED七种状态。这里在分析时我只选择比较重要和关键的状态进行分析。
线程之间的状态切换如下图所示:
下面对各种线程状态进行一些说明。
(1)新建状态(初始状态)
实现Runnable接口或者继承Thread的类便可以得到一个线程类,如果是实现Runnable接口的类,那么还需要将这个类作为参数传递给Thread类才可以作为线程启动。
(2)可运行状态
当新建状态的线程调用start()方法后便进入了可运行状态。进入可运行状态只是说明线程有机会进入运行状态,只有得到了运行的时间片才能真正得到运行并进入运行状态。
当时间片用完后或者调用yield()方法,运行中的线程会进入可运行状态。
对于锁池中的线程,如果拿到了锁,会重新进入可运行状态。
(3)运行状态
线程调度会从可运行线程池中根据一定的规则选择一个线程给其分配时间片进行运行,这也是进入运行状态的唯一方式。
(4)结束(终止)状态
当线程运行run()方法结束后或者主线程终止后,线程就会进入终止状态,当然如果在线程运行过程中如果遇到了异常也会进入终止状态。在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
(5)阻塞状态
当正在运行的线程如果(a)需要等到其他资源就绪(b)调用Thread.sleep()方法休眠一段时间(c)运行在该线程中的其他线程调用join()方法,那么当前线程都会进入阻塞状态。处于阻塞状态的线程,如果遇到(a)等待的资源已经就绪(b)休眠的时间已经到了(c)调用join()的其他线程已经结束,则此时当前线程会进入可运行状态。
(6)锁池状态
当当前线程如果去竞争一个锁而这个锁被其他线程获取了,则此时当前线程进入锁池状态。
(7)等待队列
调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。当调用wait()方法时当前线程会释放当前获取的锁,并将当前线程移入等待队列。当其他线程调用这个obj的notify()或者notifyAll()方法时,当前线程会被通知并进入到锁池中。
与等待队列相关的操作步骤和图说明如下:
至此,Java中的线程状态及状态之间的切换便大致讲述完了,谢谢。