线程和线程池
大约 6 分钟
什么是线程
百科:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
1. 创建线程的方式
1.1 继承Thread类,重写run方法
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\trunning...");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
1.2 实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\trunning...");
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
new Thread(runnable, "my-thread").start();
}
}
可以看到Runnable接口是一个函数式接口,可以使用lambda方式来快速创建线程,简化代码
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public static void main(String[] args) {
new Thread(()->System.out.println(Thread.currentThread().getName() + "\trunning..."), "my-thread").start();
}
1.3 实现Callable接口
/**
* FutureTask继承了RunnableFuture接口,RunnableFuture接口继承了Runnable接口。所有创建线程时,可以将FutureTask放如构造方法中
* FutureTask构造方法接收传入一个Callable接口的参数。
* 这样我们就可以在创建线程时使用Callable,并接收Callable接口的返回值
*/
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + "\tcall()");
return "MyCallable";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask(new MyCallable());
new Thread(futureTask).start();
// futureTask.get() 无超时限制,一致阻塞
String resultStr = futureTask.get();
// futureTask.get(long timeout, TimeUnit unit) 超时阻塞时间则不再阻塞
// String s = futureTask.get(1000, TimeUnit.MILLISECONDS);
System.out.println(resultStr);
}
}
2. 线程的启动方式
/*
* 通过调用线程的start()方法来启动线程
* start方法会调用start0()方法,这个方法是native方法。
* 具体启动时间由jvm调度
* */
Thread thread = new Thread(() -> System.out.println(Thread.currentThread()));
thread.start();
Thread.start()方法的源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
// 调用start0()方法
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
// 调用native方法启动线程
private native void start0();
3. 线程的5种状态

- 新建,线程刚被创建,还没有执行start()方法
- 就绪,线程以创建好,可以由CPU调度执行,但尚未运行
- 运行,线程正在运行,获取了CPU切片时间
- 阻塞,线程因为等待某个条件或资源而暂时无法运行,例如等待I/O操作完成或等待锁的释放。
- 死亡,线程执行完毕或因异常终止,不再参与调度。
4. 线程中断机制
- 中断只是一种协商机制,由线程自己决定是否停止。停止方法stop()已不建议使用。
- 中断的过程需要程序员自己实现,手动调用线程的interrupt()方法。
- interrupt()会将线程的中断标识设置为true,通过isInterrupted()判断标识
- 线程的静态方法Thread.interrupted() 先返回当前线程的中断标志,然后把中断标识重新设置为false,清除线程的中断状态。
线程的停止方法stop()已弃用。因为在线程运行过程中,可能还有文件IO,数据库连接等没有释放,如果突然中断线程,则可能造成内存泄漏或者其他问题。所以线程停止应交由程序自己来控制。
@Deprecated
public final void stop()
线程提供了interrupt()和isInterrupted()方法,由程序来控制线程是否停止。
public static void main(String[] args) throws Exception {
Thread myThread = new Thread(() -> {
Thread thread = Thread.currentThread();
System.out.println("thread.interrupt() before\t" + thread.isInterrupted());
while (!thread.isInterrupted()) {
}
System.out.println("thread.interrupt() after\t" + thread.isInterrupted());
});
myThread.start();
TimeUnit.SECONDS.sleep(1);
// 改变线程标志位
myThread.interrupt();
//为了程序不退出
System.in.read();
}

Thread.interrupt() 静态方法返回当前线程的中断标志,然后把中断标识重新设置为false
public static void main(String[] args) throws Exception {
Thread myThread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
System.out.println("thread.interrupt() before = " + currentThread.isInterrupted());
while (!currentThread.isInterrupted()) {
}
System.out.println("thread.interrupt() after = " + currentThread.isInterrupted());
System.out.println("Thread.interrupted() before = " + currentThread.isInterrupted());
Thread.interrupted();
System.out.println("Thread.interrupted() after " + currentThread.isInterrupted());
}, "myThread");
myThread.start();
TimeUnit.SECONDS.sleep(1);
myThread.interrupt();
System.in.read();
}
执行结果

线程池
1. 创建线程池的参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,//核心线程数
2,//最大线程数
60,//超过核心线程数线程空闲存活时间
TimeUnit.SECONDS,//存活时间单位
new ArrayBlockingQueue<>(200),//等待队列
Executors.defaultThreadFactory(),//现场生产工厂
/*
* 拒绝策略,当现场数最大,队列最大时执行的策略
* AbortPolicy 默认拒绝策略,拒绝任务并抛出任务
* CallerRunsPolicy 谁提交谁执行
* DiscardPolicy 直接拒绝任务,不抛出错误
* DiscardOldestPolicy 触发拒绝策略,只要还有任务新增,一直会丢弃阻塞队列的最老的任务,并将新的任务加入
* */
new ThreadPoolExecutor.CallerRunsPolicy()
);
2. 线程池的执行原理

- 判断核心线程数
- 小于核心线程数,创建线程
- 否则继续判断队列长度
- 判断队列长度
- 队列未满,入队
- 否则判断最大线程数
- 判断最大线程数
- 小于最大线程,创建新线程处理
- 走拒绝策略
- 拒绝策略
3. 线程池的5种状态

RUNNING
线程池创建默认进入此状态。线程池处于运行状态,此时接收新任务并处理队列中的任务
SHUTDOWN
线程池不会接收新任务,但会继续处理已经提交的任务、包括队列里面的任务。调用shutdown方法进入此状态
STOP
线程池不接受新任务、并中断正在执行的任务、清空队列里的等待任务。调用shutdownNow进行此状态
TIDYING
所有任务终止、线程池中活动线程数量为0时进入此状态,此时会调用terminated()方法
TERMINATED
线程池彻底终止、所有任务执行完成,线程池中没有任何活动线程,这是线程的最终状态。
4. submit,execute方法的区别
接收参数不同
- execute 接收Runnable参数
- submit 可以接收Callable,Runnable参数
可捕捉异常
// execute方式 executor.execute(() -> { System.out.println(Thread.currentThread() + "\t==>running.."); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } throw new RuntimeException("ERROR"); }); // submit方式 Callable<String> callable = () -> { System.out.println(Thread.currentThread() + "\t==>running.."); throw new RuntimeException(); }; executor.submit(callable); try { //只有在调用callable.call()方法时,有异常才会抛出异常。 String call = callable.call(); System.out.println(call); } catch (Exception e) { e.printStackTrace(); }
5. 判断线程池任务是否全部执行完成
5.1 CountDownLatch工具类
// 初始化任务个数
int count = 10;
// 创建CountDownLatch工具类,构造方法接收一个int值,这个值就是代表任务数量
CountDownLatch latch = new CountDownLatch(10);
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(count, count, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(0), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < count; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "=============>");
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 执行完任务就减少一次
latch.countDown();
System.out.println("latch.getCount()=" + latch.getCount());
});
}
try {
// 通过latch.await方法来阻塞当前线程。
// 当CountDownLatch的count减少到0时,就会唤醒
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程池任务执行完成");
5.2线程池的isTerminated方法
在线程池调用shutdown方法后(线程池不会接收新的任务,但会继续处理已有的任务),在所有任务执行完成后,会将标志位terminated设置为true。调用isTerminated()方法,可获取到标志位的值
for (int i = 0; i < 20; i++) {
executor.execute(() -> {
try {
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程执行:" + Thread.currentThraed().getName());
});
}
executor.shutdown();
// 这里就需要循环来处理,会浪费CPU的资源
while (!executor.isTerminated()) {
System.out.println("isTerminated:" + isTerminated());
}
System.out.println("执行完成");