写在前面: 本系列为多线程与高并发(即JUC
)的学习笔记。中间涉及到的其他问题,例如:JVM、操作系统等问题,都只会简单描述后便不再详解。希望仅此系列文章能提高以后复习的效率。本文的主要内容为线程的基础概念,包括线程的理解、创建方式、几种状态等。
什么是线程
直观的理解就是一个程序中不同的执行路径在同时运行。当然如果运行在同一个单核CPU上,我们所看到的同时运行其实是交替运行。
创建方式
- 继承
Thread
类,实现run
方法,调用start
方法开启线程
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello MyThread!");
}
}
public static void main(String[] args) {
new MyThread().start();
}
- 继承
Runnable
接口,实现run
方法
static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello MyRun!");
}
}
public static void main(String[] args) {
new Thread(new MyRunnable()).start();
}
- 直接用
Thread
的lamda
表达式
public static void main(String[] args) {
new Thread(()->{
System.out.println("Hello Lambda!");
}).start();
}
- 使用线程池
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Hello Callable";
}
}
public static void main(String[] args) {
Executors.newCachedThreadPool().submit(new MyRunnable());
Executors.newCachedThreadPool().submit(() -> {
System.out.println("Hello Lambda!");
});
Executors.newCachedThreadPool().submit(new MyCallable());
Executors.newCachedThreadPool().submit(() -> "Hello Callable");
}
FutureTask
FutureTask<Integer> task = new FutureTask<>(()->{
return 1000;
});
new Thread(task).start();
FutureTask
实现了Runable
接口,所以此方法可归并为上述方法2
线程常见的方法
sleep
sleep
表示让当前线程暂停一段时间,将CPU让给别的线程。睡眠时间内CPU运行其他线程。
static void testSleep() {
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
yield
yield
将线程退出一下,重新回到等待队列。
static void testYield() {
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i % 10 == 0) {
Thread.yield();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i % 10 == 0) {
Thread.yield();
}
}
}).start();
}
从上述程序输出结果中发现,两个线程并没有交替输出内容,所以yield
只是将线程让出CPU一下下,至于后续谁抢到CPU资源无法控制。
join
join
表示等待另一个线程结束后再运行当前线程。
static void testJoin() {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
}
上述t2
线程会等待t1
运行结束后再继续运行。
线程的状态
- NEW:线程刚刚创建,还没有启动
- RUNNABLE:可运行状态,由线程调度器可以安排执行。包括READY和RUNNING两种细分状态
- WAITING:等待被唤醒
- TIMED WAITING:隔一段时间后自动唤醒
- BLOCKED:被阻塞,正在等待锁
- TERMINATED:线程结束
interrupt
interrupt
译为线程的“打断”,但是实际上并不是真正意义上的打断线程,只是对于线程标志位的操作。
interrupt
相关的三个方法:
interrupt()
:实例方法t.interrupt()
,设置线程中断标志为true,即打扰一下线程处理一下中断isInterrupted()
:实例方法t.isInterrupted()
,查询标志位是否被设置interrupted()
:静态方法Thread.interrupted()
,查看当前线程标志位是否为true,为ture则恢复标志位为false
举两个例子验证上面的描述:
public class TestInterrupt {
static void interruptAndIsInterrupted() {
Thread t = new Thread(() -> {
for (; ; ) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Thread is interrupted!");
System.out.println(Thread.currentThread().isInterrupted());
break;
}
}
});
t.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
static void interruptAndInterrupted() {
Thread t = new Thread(() -> {
for (; ; ) {
if (Thread.interrupted()) {
System.out.println("Thread is interrupted!");
System.out.println(Thread.interrupted());
break;
}
}
});
t.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
}
interrupt
与sleep()
和wait()
sleep()
不到时间线程不会继续运行,wait()
不被唤醒线程也不会继续运行,这时候使用interrupt
也并不会让线程苏醒。但是可以catch InterruptedException
异常来处理后续的逻辑,且此时会中断标志会自动复位。举个例子:
public class TestInterruptWithSleepOrWait {
static void TestInterruptAndSleep() {
Thread t = new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("Thread is interrupted!");
System.out.println(Thread.currentThread().isInterrupted());
}
});
t.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
static void TestInterruptAndWait() {
final Object o = new Object();
Thread t = new Thread(() -> {
synchronized (o) {
try {
o.wait();
} catch (InterruptedException e) {
System.out.println("Thread is interrupted!");
System.out.println(Thread.currentThread().isInterrupted());
}
}
});
t.start();
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
}
interrupt
无法中断正在竞争锁的线程
public static void main(String[] args) {
final Object o = new Object();
Thread t1 = new Thread(() -> {
synchronized (o) {
SleepHelper.sleepSeconds(10);
}
});
t1.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(() -> {
synchronized (o) {
}
System.out.println("t2 end!");
});
t2.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt();
}
可以发现t2.interrupt()
后并没有阻止t2
获取到锁。所以interrupt()
不能打断正在竞争锁的线程。
如果想打断正在竞争锁的线程,可以使用ReentrantLock
的lockInterruptibly()
。可以参考后面的文章:juejin.cn/post/706744…