Java多线程——Thread类
Java多线程——Thread类
Java 中线程实现方式有两种:
- 继承Thread类,并重写run方法
- 实现Runnable接口的run方法
Thread类
使用方法:继承Thread类,并重写run方法
public class Demo {
public static class MyThread extends Thread {
public void run() {
System.out.println("my thread");
}
}
public static void main(String[] args) {
Thread mythread = new MyThread();
// 调用start()方法后,该线程才算启动
mythread.start();
}
}
Runable 接口
使用方法:实现Runnable接口的run方法
public class Demo1 {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
new Demo.MyThread().start();
// Java 8 函数式编程,可以省略MyThread类
new Thread(() -> {
System.out.println("Java 8 匿名内部类");
}).start();
}
}
Java线程状态
Thread 类里有一个枚举类型 State,定义了线程的几种状态,分别有:
- NEW:表示当前线程尚未启动,NEW状态表示实例化一个线程之后,但没有开始执行,即 Thread 实例还没调用 start() 方法。
- RUNNABLE:表示当前线程正在运行中,RUNNABLE 状态包括了操作系统线程状态中的 Running 和 Ready,处于此状态的线程可能正在运行,也可能正在等待系统资源。
- BLOCKED:表示当前线程处于阻塞状态,处于 BLOCKED 状态的线程正等待锁的释放以进入同步区。
- WAITING:表示当前线程处于无限期等待状态,处于这种状态的线程不会被分配CPU执行时间,它们要等待显示的被其它线程唤醒。
- TIMED_WAITING:表示当前线程处于超时等待状态,处于这种状态的线程也不会被分配 CPU 执行时间,不过无需等待被其它线程显示的唤醒,在一定时间之后它们会由系统自动的唤醒。
- TERMINATED:表示当前线程处于终止状态,已终止线程的线程状态,线程已经结束执行,即 run() 方法执行完成。
Java8 中 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;
}
查看线程当前状态可以调用 getState() 方法,并进入:
/**
* Returns the state of this thread.
* This method is designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return this thread's state.
* @since 1.5
*/
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
// sun.misc.VM 源码:
public static State toThreadState(int var0) {
if ((var0 & 4) != 0) {
return State.RUNNABLE;
} else if ((var0 & 1024) != 0) {
return State.BLOCKED;
} else if ((var0 & 16) != 0) {
return State.WAITING;
} else if ((var0 & 32) != 0) {
return State.TIMED_WAITING;
} else if ((var0 & 2) != 0) {
return State.TERMINATED;
} else {
return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
}
}
NEW:
实例化一个线程之后,并且这个线程没有开始执行,这个时候的状态就是 NEW,尚未启动指的是还没调用 Thread 实例的 start() 方法。
关于start() 的两个引申问题:
- 反复调用同一个线程的start()方法是否可行?
- 假如一个线程执行完毕(此时处于TERMINATED状态),再次调用这个线程的start()方法是否可行?
查看 start() 方法代码:
/**
* 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() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
在 start() 内部,这里有一个 threadStatus 的变量。如果它不等于0,调用 start() 是会直接抛出异常的。在调用一次start()之后,threadStatus 的值会改变(threadStatus !=0),此时再次调用 start() 方法会抛出 IllegalThreadStateException 异常。
因此,两个问题的答案都是不可行。
RUNNABLE:
表示当前线程正在运行中。处于RUNNABLE状态的线程在Java虚拟机中运行,也有可能在等待其他系统资源(比如I/O)。
RUNNABLE 状态也可以理解为存活着正在尝试征用 CPU 的线程(有可能这个瞬间并没有占用CPU,但是它可能正在发送指令等待系统调度)。由于在真正的系统中,并不是开启一个线程后,CPU就只为这一个线程服务,它必须使用许多调度算法来达到某种平衡,不过这个时候线程依然处于RUNNABLE状态。
BLOCKED:
BLOCKED 称为阻塞状态,原因通常是它在等待一个“锁”,当尝试进入一个 synchronized 语句块/方法时,锁已经被其它线程占有,就会被阻塞,直到另一个线程走完临界区或发生了相应锁对象的 wait() 操作后,它才有机会去争夺进入临界区的权利。
处于 BLOCKED 状态的线程,即使对其调用 thread.interrupt() 也无法改变其阻塞状态,因为 interrupt() 方法只是设置线程的中断状态,即做一个标记,不能唤醒处于阻塞状态的线程。
注意:
ReentrantLock.lock() 操作后进入的是 WAITING 状态,其内部调用的是 LockSupport.park() 方法。
WAITING:
处于这种状态的线程不会被分配CPU执行时间,它们要等待显示的被其它线程唤醒。WAITING 状态通常是指一个线程拥有对象锁后进入到相应的代码区域后,调用相应的“锁对象”的 wait() 方法操作后产生的一种结果。变相的实现还有 LockSupport.park()、Thread.join()等,它们也是在等待另一个事件的发生,也就是描述了等待的意思。
以下方法会让线程陷入无限期等待状态:
-
没有设置timeout参数的Object.wait()
-
没有设置timeout参数的Thread.join()
-
LockSupport.park()
注意:
LockSupport.park(Object blocker) 会挂起当前线程,参数blocker是用于设置当前线程的“volatile Object parkBlocker 成员变量”
parkBlocker 是用于记录线程是被谁阻塞的,可以通过LockSupport.getBlocker()获取到阻塞的对象,用于监控和分析线程用的。
“阻塞”与“等待”的区别:
-
“阻塞”状态是等待着获取到一个排他锁,进入“阻塞”状态都是被动的,离开“阻塞”状态是因为其它线程释放了锁,不阻塞了;
-
“等待”状态是在等待一段时间 或者 唤醒动作的发生,进入“等待”状态是主动的
如主动调用 Object.wait(),如无法获取到 ReentraantLock,主动调用 LockSupport.park(),如主线程主动调用 subThread.join(),让主线程等待子线程执行完毕再执行。离开“等待”状态是因为其它线程发生了唤醒动作或者到达了等待时间。
TIMED_WAITING:
处于这种状态的线程也不会被分配CPU执行时间,不过无需等待被其它线程显示的唤醒,在一定时间之后它们会由系统自动的唤醒。
以下方法会让线程进入TIMED_WAITING限期等待状态:
-
Thread.sleep()方法
-
设置了timeout参数的Object.wait()方法
-
设置了timeout参数的Thread.join()方法
-
LockSupport.parkNanos()方法
-
LockSupport.parkUntil()方法
TERMINATED:
已终止线程的线程状态,线程已经结束执行。这个状态仅仅是 Java 语言提供的一个状态,在操作系统内部可能已经注销了相应的线程,或者将它复用给其他需要使用线程的请求,而在Java语言级别只是通过Java代码看到的线程状态而已。
线程状态的转换
线程状态转换图:
BLOCKED状态与RUNNABLE状态的转换
处于BLOCKED状态的线程是因为在等待锁的释放。假如这里有两个线程a和b,a线程提前获得了锁并且暂未释放锁,此时b就处于BLOCKED状态。
同时,run() 方法的执行是需要时间的,并不是启动 start() 方法后就会立即执行,查看以下例子:
public class Demo2 {
public void blockedTest() {
Thread a = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
}, "a");
Thread b = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
}, "b");
a.start();
b.start();
System.out.println(a.getName() + ":" + a.getState()); // 输出?
System.out.println(b.getName() + ":" + b.getState()); // 输出?
}
// 同步方法争夺锁
private synchronized void testMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
demo2.blockedTest();
}
}
输出结果:此时 a 线程已得到锁,进入 run() 方法,但还没有执行 Thread.sleep(),由于 a 线程已得到锁,b 线程等待锁的释放,进入 BLOCKED 状态。
a:RUNNABLE
b:BLOCKED
Process finished with exit code 0
调换语句顺序,发现结果发生变化:
public void blockedTest() throws InterruptedException {
... ...
a.start();
System.out.println(a.getName() + ":" + a.getState()); // 输出?
b.start();
System.out.println(b.getName() + ":" + b.getState()); // 输出?
}
输出结果:原因是测试方法的main线程只保证了a,b两个线程调用start()方法(转化为RUNNABLE状态),还没等两个线程真正开始争夺锁,就已经打印此时两个线程的状态(RUNNABLE)了。
a:RUNNABLE
b:RUNNABLE
Process finished with exit code 0
让 main 线程停一段时间,就可以得到 BLOCKED 状态的结果:
public void blockedTest() throws InterruptedException {
... ...
a.start();
Thread.sleep(200L);
System.out.println(a.getName() + ":" + a.getState()); // 输出?
b.start();
Thread.sleep(200L);
System.out.println(b.getName() + ":" + b.getState()); // 输出?
}
输出结果:此时 a 线程已得到锁,正在执行 run() 方法,进入 TIMED_WAITING 状态,b 线程由于得不到锁,进入 BLOCKED 状态
a:TIMED_WAITING
b:BLOCKED
Process finished with exit code 0
WAITING状态与RUNNABLE状态的转换
有3个方法可以使线程从RUNNABLE状态转为WAITING状态。
Thread.join():
public void blockedTest() {
······
a.start();
a.join();
b.start();
System.out.println(a.getName() + ":" + a.getState()); // 输出 TERMINATED
System.out.println(b.getName() + ":" + b.getState());
}
输出结果:调用join()方法不会释放锁,会一直等待当前线程执行完毕(转换为TERMINATED状态)。b线程的状态,有可能打印RUNNABLE(尚未进入同步方法),也有可能打印TIMED_WAITING(进入了同步方法)。
a:TERMINATED
b:TIMED_WAITING
Process finished with exit code 0
需要注意的是,其他线程调用notify()方法只会唤醒单个等待锁的线程,如有有多个线程都在等待这个锁的话不一定会唤醒到之前调用wait()方法的线程。
Object.wait():导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
使用示例:
public class WaitNotifyTest {
public static void main(String[] args) {
Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A等待获取lock锁");
synchronized (lock) {
try {
System.out.println("线程A获取了lock锁");
Thread.sleep(1000);
System.out.println("线程A将要运行lock.wait()方法进行等待");
lock.wait();
System.out.println("线程A等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B等待获取lock锁");
synchronized (lock) {
System.out.println("线程B获取了lock锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B将要运行lock.notify()方法进行通知");
lock.notify();
}
}
}).start();
}
}
输出结果:
线程A等待获取lock锁
线程A获取了lock锁
线程B等待获取lock锁
线程A将要运行lock.wait()方法进行等待
线程B获取了lock锁
线程B将要运行lock.notify()方法进行通知
线程A等待结束
Process finished with exit code 0
LockSupport.park():LockSupport.park() 调用的是 Unsafe 中的 native 代码,与Object类的wait/notify机制相比,park 以 thread 为操作对象更符合阻塞线程的直观定义,操作更精准,可以准确地唤醒某一个线程增加了灵活性。
//LockSupport中
public static void park() {
UNSAFE.park(false, 0L);
}
TIMED_WAITING状态与RUNNABLE状态转换
TIMED_WAITING 与 WAITING 状态类似,只是 TIMED_WAITING 状态等待的时间是指定的。
Thread.sleep(long):
使当前线程睡眠指定时间。需要注意这里的“睡眠”只是暂时使线程停止执行,并不会释放锁。时间到后,线程会重新进入RUNNABLE状态。
Sleep 方法代码:
public static native void sleep(long millis) throws InterruptedException;
// 纳秒级别控制
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
Object.wait(long):
wait(long) 方法使线程进入 TIMED_WAITING 状态。这里的wait(long)方法与无参方法 wait() 相同的地方是,都可以通过其他线程调用 notify() 或 notifyAll() 方法来唤醒。
不同的地方是,有参方法 wait(long) 就算其他线程不来唤醒它,经过指定时间 long 之后它会自动唤醒,拥有去争夺锁的资格。
Thread.join(long):
join(long) 使当前线程执行指定时间,并且使线程进入 TIMED_WAITING 状态。
join(long) 的使用:调用 a.join(1000L),因为是指定了具体 a 线程执行的时间的,并且执行时间是小于 a 线程 sleep 的时间,所以 a 线程状态输出 TIMED_WAITING,b 线程状态仍然不固定(RUNNABLE 或 BLOCKED)。
public void blockedTest() {
······
a.start();
a.join(1000L);
b.start();
System.out.println(a.getName() + ":" + a.getState()); // 输出 TIEMD_WAITING
System.out.println(b.getName() + ":" + b.getState());
}
Java线程中断
Thread类里提供的关于线程中断的几个方法:
- Thread.interrupt():中断线程。这里的中断线程并不会立即停止线程,而是设置线程的中断状态为true(默认是flase);
- Thread.interrupted():测试当前线程是否被中断。线程的中断状态受这个方法的影响,意思是调用一次使线程中断状态设置为true,连续调用两次会使得这个线程的中断状态重新转为false;
- Thread.isInterrupted():测试当前线程是否被中断。与上面方法不同的是调用这个方法并不会影响线程的中断状态。
“VisualVM线程监控线程状态”与“Java线程状态”对应关系总结:
通过 dump thread stack,并与 VisualVM 监控信息中的线程名称对应,找到的 VisualVM 每种线程状态的线程堆栈如下:
1、运行:RUNNABLE
"http-bio-8080-Acceptor-0" daemon prio=6 tid=0x000000000d7b4800 nid=0xa264 runnable [0x000000001197e000]
java.lang.Thread.State: RUNNABLE
at java.net.DualStackPlainSocketImpl.accept0(Native Method)
at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
- locked <0x00000000c2303850> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:530)
at java.net.ServerSocket.accept(ServerSocket.java:498)
at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
2、休眠:sleeping
"Druid-ConnectionPool-Destory-293325558" daemon prio=6 tid=0x000000000d7ad000 nid=0x9c94 waiting on condition [0x000000000bf0f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:1685)
Locked ownable synchronizers:
- None
3、等待:wait
"Finalizer" daemon prio=8 tid=0x0000000009349000 nid=0xa470 in Object.wait() [0x000000000a82f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue.Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Locked ownable synchronizers:
- None
"JMX server connection timeout 45" daemon prio=6 tid=0x000000000e846000 nid=0xab10 in Object.wait() [0x00000000137df000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000c55da3f0> (a [I)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x00000000c55da3f0> (a [I)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
4、驻留:park
"http-bio-8080-exec-2" daemon prio=6 tid=0x000000000d7b8000 nid=0x9264 waiting on condition [0x000000000ee4e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c5629bc8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor.Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread.WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"pool-9-thread-1" prio=6 tid=0x000000000d7b2000 nid=0xd5fc waiting on condition [0x000000001187e000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c563b9e0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
at java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1090)
at java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:807)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor.Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
5、监视:monitor
"Thread-1" prio=6 tid=0x000000000a8a1800 nid=0xfdb4 waiting for monitor entry [0x000000000b4de000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.Test2$T.run(Test2.java:58)
- waiting to lock <0x00000000eab757e0> (a java.lang.Object)
Locked ownable synchronizers:
- None
汇总如下图所示: