1.4 线程的状态
sleep、yield 和 join 区别:
-
sleep 执行后线程进入阻塞状态,当前线程休眠一段时间
-
yield 执行后线程进入就绪状态,使当前线程和所有等待的线程一起进行竞争 CPU 资源
-
join执行线程进入阻塞状态,t.join 表示阻塞调用此方法的线程,直到线程 t 完成,方可继续执行。底层实际调用 wait 方法
-
新建状态(New):线程对象被创建后,就进入了新建状态。例如:Thread thread = new Thread()
-
就绪状态(Runnable):也被称为"可执行状态"。线程对象呗创建后,其它线程调用了该对象的 start() 方法,从而就启动该线程。例如T.stat(),处于就绪状态的线程,随时可能被CPU调度执行
-
运行状态(Running):线程获取 CPU 权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态
-
阻塞状态(Blocked):阻塞状态是线程放弃CPU使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
-
等待阻塞:通过调用线程的wait() 方法,让线程等待某工作的完成
-
同步阻塞:线程在获取 synchronized 同步锁失败,它会进入同步阻塞状态
-
其它阻塞:通过调用线程的 sleep() 或 join() 或发出 I/O 请求时,线程会进入到阻塞状态。当 sleep() 状态超时,join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入到就绪状态
-
死亡状态(Dead):线程执行完了或者因异常退出了 run() 方法,该线程结束生命周期
2.多线程的实现方式
==========
2.1继承 Thread 类创建线程
Thead 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例
1 /**
2 * @description: 多线程实现方法1:集成Thread类
3 * @author: DZ
4 **/
5 @Slf4j
6 public class MyThread1 extends Thread {
7 @Override
8 public void run() {
9 log.info(“MyThread1”);
10 log.info(“MyThread2”);
11 }
12
13 public static void main(String[] args) {
14 MyThread1 t1 = new MyThread1();
15 MyThread1 t2 = new MyThread1();
16 t1.start();
17 t2.start();
18 }
19 }
2.2实现 Runnable 接口创建线程
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时可以通过实现 Runnable 接口
避免单继承的局限性、适合多个相同的线程去处理同一个资源
1 /**
2 * @description: 多线程实现方法2:实现Runnable接口
3 **/
4 @Slf4j
5 public class MyThread2 implements Runnable {
6
7 @Override
8 public void run() {
9 log.info(“MyThread1”);
10 log.info(“MyThread2”);
11 }
12
13 public static void main(String[] args) {
14 MyThread2 m = new MyThread2();
15 //1.调用run方法
16 Thread t1 = new Thread(m);
17 Thread t2 = new Thread(m);
18 t1.start();
19 t2.start();
20 }
21 }
2.3实现 Callable 接口,通过 Future Task 包装器来创建 Thread 线程
可以获取线程的返回值
1 /**
2 * @description: 多线程实现方法2:实现Callable接口
3 * @author: DZ
4 **/
5 @Slf4j
6 public class MyThread3 implements Callable {
7 @Override
8 public String call() throws Exception {
9 log.info(“MyThread1”);
10 log.info(“MyThread2”);
11 return “MyThread3”;
12 }
13
14 public static void main(String[] args) throws ExecutionException, InterruptedException {
15 MyThread3 m = new MyThread3();
16 //存储返回值,其中泛型为返回值的类型
17 FutureTask futureTask = new FutureTask<>(m);
18 new Thread(futureTask).start();
19 System.out.println(futureTask.get());
20 }
21
22 }
2.4通过线程池
2.4.1 线程池的主要参数
1 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
2 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
3 Executors.defaultThreadFactory(), defaultHandler);
4 }
- corePoolSize
当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
- maximumPoolSize
线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数
- keepAliveTime
当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数
- workQueue
用于传输和保存等待执行任务的阻塞队列
-
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列
-
LinkedBlockingQueue 一个由链表结构组成的有界阻塞队列
-
PriorityBlockingQueue 一个支持优先级排序的无界阻塞队列
-
DelayQueue 一个使用优先级队列实现的无界阻塞队列
-
SynchronousQueue 一个不存储元素的阻塞队列
-
LinkedTransferQueue 一个由链表结构组成的无界阻塞队列
-
LinkedBlockingDeque 一个由链表结构组成的双向阻塞队列
**作用:**阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。使得在线程不至于一直占用cpu资源。
- threadFactory
用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)
- handler
当线程池和队列都满了,再加入线程会执行此策略
AbortPolicy: 直接抛出异常,阻止线程正常运行
1 public static class AbortPolicy implements RejectedExecutionHandler {
2 public AbortPolicy() {}
3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
4 throw new RejectedExecutionException(“Task " + r.toString() +” rejected from " + e.toString());
5 }
6 }
CallerRunsPolicy: 直接在方法的调用线程中执行,除非线程池已关闭
1 public static class CallerRunsPolicy implements RejectedExecutionHandler {
2 public CallerRunsPolicy() {}
3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
4 if (!e.isShutdown()) {
5 r.run();
6 }
7 }
8 }
DiscardPolica: 丢弃当前的线程任务而不做任何处理。如果系统允许在资源不足的情况下弃部分任务,则这将是保障系统安全、稳定的一种很好的方案
1 public static class DiscardPolicy implements RejectedExecutionHandler {
2 public DiscardPolicy() {}
3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
4 }
5 }
DiscardOlderPolicy: 移除线程队列中最早(老)的一个线程任务,并尝试提交当前任务
1 public static class DiscardOldestPolicy implements RejectedExecutionHandler {
2 public DiscardOldestPolicy() { }
3 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
4 if (!e.isShutdown()) {
5 e.getQueue().poll();// 最早(老)的任务出队列
6 e.execute®;
7 }
8 }
9 }
2.4.2如何设置线程池
- CPU密集型
尽量使用较小的线程池,一般为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,会造成CPU过度切换。
- IO密集型任务
可以使用稍大的线程池,一般为2*CPU核心数。 IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候有其他线程去处理别的任务,充分利用CPU时间
- 混合型任务
线程数 = CPU核心数 * (1+平均等待时间 / 平均工作时间)
2.4.3 代码示例
1 import lombok.extern.slf4j.Slf4j;
2 import org.junit.Test;
3
4 import java.util.concurrent.*;
5
6 /**
7 * @description: 通过线程池实现多线程
8 * @author: DZ
9 **/
10 @Slf4j
11 public class MyThread4 {
12 //通常使用方式,定义前5个参数即可,其余默认
13 private ThreadPoolExecutor threadPoolExecutor0 = new ThreadPoolExecutor(5, 10, 60,
14 TimeUnit.SECONDS, new LinkedBlockingQueue(10));
15
16 //所有参数均自定义(增加工厂ThreadFactory和拒绝方式Handle)
17 private ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(5, 10, 60,
18 TimeUnit.SECONDS, new LinkedBlockingQueue(10), new ThreadFactory() {
19 @Override
20 public Thread newThread(Runnable r) {
21 Thread thread = new Thread®;
22 log.info(“我是线程{}”, thread.getName());
23 return thread;
24 }
25 }, new RejectedExecutionHandler() {
26 @Override
27 public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
28 log.info(“线程被去掉{}”, new Thread®.getName());
29 }
30 });
31
32 //所有参数均自定义,拒绝方式使用默认new ThreadPoolExecutor.AbortPolicy(),new ThreadPoolExecutor.DiscardOldestPolicy(),new ThreadPoolExecutor.CallerRunsPolicy(),new ThreadPoolExecutor.DiscardPolicy()
33 private ThreadPoolExecutor threadPoolExecutor2 = new ThreadPoolExecutor(5, 10, 60,
34 TimeUnit.SECONDS, new LinkedBlockingQueue(10), new ThreadFactory() {
35 @Override
36 public Thread newThread(Runnable r) {
37 Thread thread = new Thread®;
38 log.info(“我是线程{}”, thread.getName());
39 return thread;
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
olExecutor.DiscardPolicy()
33 private ThreadPoolExecutor threadPoolExecutor2 = new ThreadPoolExecutor(5, 10, 60,
34 TimeUnit.SECONDS, new LinkedBlockingQueue(10), new ThreadFactory() {
35 @Override
36 public Thread newThread(Runnable r) {
37 Thread thread = new Thread®;
38 log.info(“我是线程{}”, thread.getName());
39 return thread;
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
[外链图片转存中…(img-bu9wQHh6-1714354452015)]