对于 Thread
的理解,需要明白 Thread
中的所有常用的方法的含义,使用场景
可以方便的定义 thread name,daemon status,priority, thread Group
一个默认的实现java.util.concurrent.Executors.DefaultThreadFactory
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()+" start ..."));
t.start();// 启动这个新的线程
start
会调用一个start0
方法,让 jvm 启用一个线程
Causes this thread to begin execution; the Java Virtual Machine
calls the run
method of this thread.
// 这个 run 方法其实是在我们调用 Thread#start 方法之后,由 JVM 使用新的线程调用的
// JVM 保证在线程创建之后,会调用 Thread#run 方法
// 如果我们自己在代码中直接调用 Thread#run 方法,run 方法也会执行,但不是在新的线程中执行的
@Override
public void run() {
if (target != null) {
target.run();
}
}
我们知道启动一个线程是用start()
方法,但是如何关闭(安全的)一个线程呢?
使用 volatile
标记(变量) + interrupt
- volatile 变量,如果线程检查到的状态是关闭的,那么此变量不接受新的任务即可
- volatile 变量,保证可见性(一个线程修改变量的结果,对其他线程可见)
- interrupt 使阻塞(blocked)状态的线程,出现
InterruptedException
异常,终止线程
Waits for this thread to die.
- 一个线程等待另一个线程完成后,该线程继续执行
- join 实现的是
wait()
+notifyAll
(notify
)
public static void main(String[] args) throws Exception {
Runnable r = () -> {
System.out.println("run ...");
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run end ...");
};
Thread thread1 = new Thread(r);
Runnable r2 = () -> {
try {
System.out.println("run2 ...");
//thread1.join();
System.out.println("run2 end ...");
} catch (Exception e) {
e.printStackTrace();
}
};
Thread thread2 = new Thread(r2);
thread1.start();
thread2.start();
}
// t2 在t1之前结束
// run ...
// run2 ...
// run2 end ...
// run end ...
// use thread1.join();
// t2必在t1完成后结束(t2一直等待t1结束)
// run ...
// run2 ...
// run end ...
// run2 end ...
线程让出 cpu(别用)
Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。
Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”
// use TimeUnit
TimeUnit.MILLISECONDS.sleep(200);
// use Thread
Thread.sleep(200);
InterruptedException
是如何产生的 demo
如果一个线程在sleep
状态(wait,join,sleep),调用 interrupt 会出现InterruptedException
异常
public static void main(String[] args) throws InterruptedException {
Runnable r = () -> {
try {
System.out.println("[I am " + Thread.currentThread().getName() + "] thread");
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread t = new Thread(r);
t.start();
// throw InterruptedException
System.out.println("[I am " + Thread.currentThread().getName() + "] thread");
t.interrupt();
}
日志:
[I am main] thread
[I am Thread-0] thread
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.run(Thread.java:748)
如何正确的终止一个线程,也是一门艺术 😂
线程的状态在多数情况下对我们来说是无感知的。但是在遇到线程问题,如死锁等,需要通过 jstack
命令生成线程快照
排查问题的时候,是十分有帮助的。
jstack
日志片段:
"pool-1-thread-2" #12 prio=5 os_prio=0 tid=0x000000005b486000 nid=0x460 waiting on condition [0x000000005bd2f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d62af108> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"pool-1-thread-1" #11 prio=5 os_prio=0 tid=0x000000005b49d000 nid=0x3624 waiting on condition [0x000000005b01e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d62af108> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
上面的 ExecutorsDemo
中,我们使用 Executors.newFixedThreadPool(2)
方法创建了二个线程,从上面的日志,我们可以快速的看到
代码由于 LinkedBlockingQueue.take
方法造成阻塞.
- 实现 Runnable 接口,把 Runnable 提交给 Thread (推荐方式)
- 继承 Thread 类,重写 run 方法
// 实现 Runnable 接口
public class ThreadTest {
public static void main(String[] args) {
new Thread(() -> System.out.println("run")).start();
new ThreadRun().start();
}
}
// 继承 Thread 类,重写 run 方法
class ThreadRun extends Thread {
@Override
public void run() {
System.out.println("ThreadRun");
}
}