线程状态及转换
新建(New)
创建后尚未启动
运行(Runnable)
可能正在运行,也可能正在等待 CPU 时间片。
包含了操作系统线程状态中的 Running 和 Ready。
阻塞(Blocked)
等待获取一个排它锁,如果其线程释放了锁就会结束此状态。
无限期等待(Waiting)
等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。
进入方法:
- 没有设置 Timeout 参数的 Object.wait() 方法。
- 没有设置 Timeout 参数的 Thread.join() 方法。
对应的退出方法:
- Object.notify() / Object.notifyAll()。
- 等待调用的线程执行完毕。
有限期等待(Timed Waiting)
无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。
阻塞和等待的区别在于,阻塞是被动的,它是在等待获取一个排它锁。而等待是主动的,通过调用 Thread.sleep() 和 Object.wait() 等方法进入。
进入方法:
-
Thread.sleep() 方法。
-
设置了 Timeout 参数的 Object.wait() 方法。
-
设置了 Timeout 参数的 Thread.join() 方法。
对应的退出方法:
-
时间结束。
-
时间结束 / Object.notify() / Object.notifyAll()。
-
时间结束 / 等待调用的线程执行完毕。
死亡(Terminated)
可以是线程结束任务之后自己结束,或者产生了异常而结束 。
Thread的state枚举类源码
/**
* 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;
}
线程创建方式
一般来说有三种使用线程的方法:
-
继承 Thread 类。
-
实现 Runnable 接口。
-
实现 Callable 接口。
但是我们需要注意,实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,而不是真正意义上的线程。
线程的启动是要调用 start 方法,而 start 方法始终是从 Thread 的 run 方法开始执行的,这也是 Runnable 和 Callable 最后都得交给 Thread 启动的原因。
所以我的理解是:Thread是线程类,另外两个是任务类。
附上一段源码:
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
~~~~~~~~~
}
继承 Thread 类
比较粗暴,直接继承 Thread 类,然后重写 run 方法。
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
public class MyThread extends Thread {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
为啥说这种方式比较粗暴,我们看看Thread类的run方法源码。
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
从源码可以看出来,Thread 更希望我们传入一个 Runnable 类型的对象,然后来调用这个
Runnable 对象的 run 方法,达到一种 线程与任务 分离的效果。而不是希望我们通过继承来粗暴的重写。
实现 Runnable 接口
实现 run() 方法,然后将 Runnable 子类传递给 Thread 类。
public class MyRunnable implements Runnable {
public void run() {
// ...
}
}
public static void main(String[] args) {
MyRunnable instance = new MyRunnable();
Thread t = new Thread(instance);
t.start();
}
这里需要实现 Runnable#run() 的原因有两个:
- 因为 Thread#run() 里面调用了 target.run()。
- Runnable是接口,也强制必须重写他的方法。
这里大家肯定觉得明明第二个理由就已经是必要的条件了,为啥还要第一个理由。
我们知道 Thread 的 run 方法是线程的起点。这个时候如果我们需要分开线程和任务,并且我们 Thread#run() 里面必须要调用一个固定的方法来执行任务类,那该怎么做呢?
那就是将 run() 方法向上抽取, 做成抽象方法 ,让每个任务类都强制去实现这个方法。
也就是说我们是因为 Thread#run() 需要那个任务类有固定的方法,所以我们抽出了 Runnable#run()。
实现 Callable 接口
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。
public class MyCallable implements Callable<Integer> {
public Integer call() {
return 123;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread t = new Thread(ft);
t.start();
System.out.println(ft.get());
}
一些线程机制
Executor
Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。 也就是我们常说的线程池。
线程池的好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
线程池核心类ThreadPoolExecutor主要参数
- corePoolSize:核心线程数量
- maximumPoolSize:线程最大线程数
- keepAliveTime:线程没有任务执行时最多保持多久时间终止(当线程中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交核心线程外的线程不会立即销毁,而是等待,直到超过keepAliveTime)
- unit:keepAliveTime的时间单位。
- workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响。
- threadFactory:线程工厂,用来创建线程,有一个默认的工场来创建线程,这样新创建出来的线程有相同的优先级,是非守护线程、设置好了名称)。
- rejectHandler:当拒绝处理任务时(阻塞队列满)的策略。
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
主要的四种创建方法
-
Executors.newCachedThreadPool: 一个任务创建一个线程;
-
Executors.newFixedThreadPool: 所有任务只能使用固定大小的线程;
-
Executors.newSingleThreadExecutor: 相当于大小为 1 的 FixedThreadPool。
-
Executors.newScheduledThreadPoll: 定长线程池,核心线程数由用户传入,支持定时和周期任务执行
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new MyRunnable());
}
executorService.shutdown();
}
Daemon
守护线程是程序运行时在后台提供服务的线程,属于程序中不可或缺的部分。
当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。
main() 属于非守护线程。
使用 setDaemon() 方法将一个线程设置为守护线程。
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.setDaemon(true);
}
sleep()
Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒。
sleep() 可能会抛出 InterruptedException,因此必须使用try-catch进行处理。线程中抛出的其它异常也同样需要使用try-catch进行处理。
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
线程互斥同步
在Java中,主要是使用 JVM 实现的 synchronized,和 JDK 实现的 ReentrantLock。
synchronized
1. 同步类
作用于整个类,如果说多个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。
public void func() {
synchronized (类名.class) {
// ...
}
}
2. 同步方法
public synchronized void func () {
// ...
}
3. 同步代码块
它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。
public void func() {
synchronized (this) {
// ...
}
}
4. 同步静态方法
会作用于整个类。
public synchronized static void fun() {
// ...
}
ReentrantLock
ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁。
比 synchronized 更灵活,只需要在需要的代码块上使用 lock() 和 unlock() 包裹即可。
public class LockExample {
private Lock lock = new ReentrantLock();
public void func() {
lock.lock();
try {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
} finally {
lock.unlock(); // 确保释放锁,从而避免发生死锁。
}
}
}
synchronized 和 ReentrantLock 比较
线程之间的协作
当有多个线程可以需要一起工作去解决某个问题时,那他们肯定就有执行的先后之分,那么这时候就需要对线程进行协调 。
join()
在线程中调用另一个线程的 join() 方法,会将当前线程挂起,直到目标线程结束。
public class JoinExample {
private class A extends Thread {
@Override
public void run() {
System.out.println("A");
}
}
private class B extends Thread {
private A a;
B(A a) {
this.a = a;
}
@Override
public void run() {
try {
a.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
}
}
public void test() {
A a = new A();
B b = new B(a);
b.start();
a.start();
}
}
以上代码,虽然 b 线程先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法,b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。
wait() notify() notifyAll()
调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。
值得一提的事,它们都属于 Object 的方法,而不是 Thread 的方法。
只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateExeception。
使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。
wait() 和 sleep() 的区别
- wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
- wait() 会释放锁,sleep() 不会。
await() signal() signalAll()
java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。