@Slf4j
public class MyThread1 {
public static void main(String[] args) {
new Thread(()->{
log.debug("t1 thread running");
},"t1").start();
log.debug("main thread running");
}
}
20:11:09.740 [main] DEBUG juc.thread.MyThread1 - main thread running
20:11:09.740 [t1] DEBUG juc.thread.MyThread1 - t1 thread running
@Slf4j
public class MyThread2 {
public static void main(String[] args) {
Runnable runnable = ()->log.debug("t2 thread running");
Thread thread = new Thread(runnable,"t2");
thread.start();
log.debug("main thread running");
}
}
10:15:20.385 [t2] DEBUG juc.thread.MyThread2 - t2 thread running
10:15:20.385 [main] DEBUG juc.thread.MyThread2 - main thread running
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
@Slf4j
public class MyThread3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(()->{
log.debug("t3 thread running");
Thread.sleep(3000);
return 3;
});
Thread thread = new Thread(futureTask,"t3");
thread.start();
//阻塞等待结果返回
Integer integer = futureTask.get();
log.debug("t3 result is"+integer);
}
}
10:54:23.627 [t3] DEBUG juc.thread.MyThread3 - t3 thread running
10:54:26.631 [main] DEBUG juc.thread.MyThread3 - t3 result is3
JVM 中由堆、栈、方法区所组成,其中栈内存是给线程用的,每个线程启动后,虚拟机就会为其分配一块栈内存:
因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码;
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址
,是线程私有的
@Slf4j
public class MyThread4 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
log.debug("enter sleep");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
log.debug("wake up");
e.printStackTrace();
}
});
thread.start();
Thread.sleep(1000);
log.debug("interrupt");
thread.interrupt();
}
}
17:04:34.423 [Thread-0] DEBUG juc.thread.MyThread4 - enter sleep
17:04:35.419 [main] DEBUG juc.thread.MyThread4 - interrupt
17:04:35.420 [Thread-0] DEBUG juc.thread.MyThread4 - wake up
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at juc.thread.MyThread4.lambda$main$0(MyThread4.java:20)
at java.lang.Thread.run(Thread.java:748)
线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
@Slf4j
public class TestJoin {
static int r = 0;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
sleep(1);
log.debug("结束");
r = 10;
},"t1");
t1.start();
t1.join();
log.debug("结果为:{}", r);
log.debug("结束");
}
}
让线程按顺序执行,调用join方法,主线程会让t1线程加入到当前线程,执行完才会继续往下执行。
@Slf4j
public class TestJoin {
static int r = 0;
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test2();
}
private static void test2() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(1);
r1 = 10;
});
Thread t2 = new Thread(() -> {
sleep(2);
r2 = 20;
});
t1.start();
t2.start();
long start = System.currentTimeMillis();
log.debug("join begin");
t2.join();
log.debug("t2 join end");
t1.join();
log.debug("t1 join end");
long end = System.currentTimeMillis();
//17:46:36.413 [main] DEBUG juc.thread.TestJoin - r1: 10 r2: 20 cost: 2003
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
}
第一个 join:等待 t1 时, t2 并没有停止, 而在运行
第二个 join:1s 后, 执行到此, t2 也运行了 1s, 因此也只需再等待 1s
@Slf4j
public class TestJoin {
static int r = 0;
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test3();
}
public static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(2);
r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
// 线程执行结束会导致 join 结束
log.debug("join begin");
//17:52:32.167 [main] DEBUG juc.thread.TestJoin - r1: 0 r2: 0 cost: 1510
//没等够时间,没赋值成功
t1.join(1500);
//17:50:56.836 [main] DEBUG juc.thread.TestJoin - r1: 10 r2: 0 cost: 2003
//只需要等2s 给了3s 也只会等2s
//t1.join(3000);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
}
阻塞状态的下的线程,如果被打断,打断标志位会被清空
sleep,wait,join 这几个方法都会让线程进入阻塞状态,打断标志位清空
@Slf4j
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("sleep...");
try {
//sleep, wait, join 会在被打断之后把打断标志位清空
Thread.sleep(5000); //
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
//false
log.debug("打断标记:{}", t1.isInterrupted());
}
}
线程被打断,捕获到异常,就会将标志位置为false
打断正常运行的线程, 被打断,不会清空打断状态
正常运行的线程被打断之后,不会终止运行,只是收到了一个被打断的标识,如果需要根据被打断做处理,需要手动写代码处理,被打断的线程自己决定是要继续运行还是停止运行。
@Slf4j
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(true) {
boolean interrupted = Thread.currentThread().isInterrupted();
if(interrupted) {
log.debug("被打断了, 退出循环");
break;
}
}
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
}
}
@Slf4j
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTerminate twoPhaseTerminate = new TwoPhaseTerminate();
twoPhaseTerminate.start();
Thread.sleep(4000);
twoPhaseTerminate.stop();
}
}
@Slf4j
class TwoPhaseTerminate {
private Thread monitorThread;
/**
* 启动线程
*/
public void start() {
monitorThread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
// 是否被打断
if (current.isInterrupted()) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000);
log.debug("执行监控记录");
} catch (InterruptedException e) {
e.printStackTrace();
//重置清除标志
current.interrupt();
}
}
}, "monitor");
monitorThread.start();
}
/**
* 停止线程
*/
public void stop() {
monitorThread.interrupt();
}
}
18:56:14.235 [monitor] DEBUG juc.thread.TwoPhaseTerminate - 执行监控记录
18:56:15.243 [monitor] DEBUG juc.thread.TwoPhaseTerminate - 执行监控记录
18:56:16.245 [monitor] DEBUG juc.thread.TwoPhaseTerminate - 执行监控记录
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at juc.thread.TwoPhaseTerminate.lambda$start$0(TestInterrupt.java:42)
at java.lang.Thread.run(Thread.java:748)
18:56:17.235 [monitor] DEBUG juc.thread.TwoPhaseTerminate - 料理后事
isInterrupted: 判断是否被打断,不会清除打断标记
interruptted: 判断当前线程是否被打断,会清除打断标记
调用park方法之后不会往下执行。一直阻塞
@Slf4j
public class ParkTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
}
}
19:03:51.383 [t1] DEBUG juc.thread.ParkTest - park...
被打断之后,标志位为true
@Slf4j
public class ParkTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
}
19:05:17.069 [t1] DEBUG juc.thread.ParkTest - park...
19:05:18.069 [t1] DEBUG juc.thread.ParkTest - unpark...
19:05:18.070 [t1] DEBUG juc.thread.ParkTest - 打断状态:true
当被打断了,标志位为true,会让park失效,再次park的时候会失效,不会阻塞:
@Slf4j
public class ParkTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
//true
log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
LockSupport.park();
log.debug("unpark...");
}, "t1");
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
}
19:08:21.353 [t1] DEBUG juc.thread.ParkTest - park...
19:08:22.350 [t1] DEBUG juc.thread.ParkTest - unpark...
19:08:22.350 [t1] DEBUG juc.thread.ParkTest - 打断状态:true
19:08:22.352 [t1] DEBUG juc.thread.ParkTest - unpark...
可以调用interrupted方法,在被打断之后,标志位被清空,再park可以阻塞:
@Slf4j
public class ParkTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
//true
//log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
log.debug("打断状态:{}", Thread.interrupted());
LockSupport.park();
log.debug("unpark...");
}, "t1");
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
}
19:11:50.823 [t1] DEBUG juc.thread.ParkTest - park...
19:11:51.823 [t1] DEBUG juc.thread.ParkTest - unpark...
19:11:51.824 [t1] DEBUG juc.thread.ParkTest - 打断状态:true
默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
@Slf4j
public class Test5 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
}
log.debug("t1结束");
}, "t1");
t1.setDaemon(true);
t1.start();
Thread.sleep(1000);
log.debug("main线程结束");
}
}
19:25:06.046 [main] DEBUG juc.thread.Test5 - main线程结束
垃圾回收器线程就是一种守护线程
Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求