多线程
一、基本概念:程序、进程、线程
- 程序,程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
- 进程,进程是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位
- 线程,通常一个进程可以包含若干个线程,当然一个进程至少有一个线程,线程是cpu调度和执行的单位
- 很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核
线程
- 线程就是独立的执行路径
- 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程
- main()称为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干涉的.
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
- 线程会带来额外开销,如cpu调度时间,并发控制开销
- java应用程序java.exe,至少有三个线程: main()主线程,gc()垃圾回收线程,异常处理线程。如果发生异常,会影响主线程
二、并行与并发
- 并行:多个cpu同时执行多个任务。比如:多个人同时做不同的事
- 并发:一个cpu(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事
三、多线程优点
- 提高应用程序的响应。对图形化界面更有意义,可增强用户体验
- 提高计算机系统cpu的利用率
- 改善程序结构。将既长又复杂的进程分位多个线程,独立运行,利于理解和修改
四、创建多线程方式一:继承Thread
/**
* 多线程的创建,方式一:继承于Thread类
* 1、创建一个继承于Thread类的子类
* 2、重写Thread类的run()方法
* 3、创建Thread类的子类对象
* 4、通过此对象调用start()
* 4.1、启动当前线程
* 4.2、调用当前线程的run()
*/
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":::"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":::"+i);
}
}
}
}
五、线程常用方法
Thread中常用方法:
- 1、start():启动当前线程,调用当前线程的run()
- 2、run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
- 3、currentThread():静态方法,返回执行当前代码的线程
- 4、getName():获取当前线程名子
- 5、setName():设置当前线程的名字
- 6、yield():释放当前cpu的执行权
- 7、join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a 才结束阻塞状态
- 8、stop():已过时。当执行此方法时,强制结束当前线程
- 9、sleep(long millis):让当前线程“睡眠”指定的毫秒,指定的时间类,当前线程是阻塞状态
- 10、isAlive():判断当前线程是否存在
线程优先级: 优先级只是概率
MIN_PRIORITY = 1;
NORM_PRIORITY = 5; 默认优先级
MAX_PRIORITY = 10;
getPriority() -- 获取线程优先级
setPriority() -- 设置线程优先级
六、创建多线程方式二:实现Runnable接口
/**
* 1、创建一个实现了Runnable接口
* 2、实现类去实现Runnable中的抽象方法:run
* 3、创建实现类的对象
* 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5、通过Thread类的对象调用start()
*/
public class MyRun{
public static void main(String[] args) {
RunTes r = new RunTes();
Thread t = new Thread(r);
t.start();
//可直接命名
new Thread(r, "线程二").start();
}
}
class RunTes implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2==0){
System.out.println(Thread.currentThread().getName()+"::"+i);
}
}
}
}
七、创建多线程方式三:实现Callable接口 jdk5.0
- 与Runnable相比,Callable功能更强大
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
/**
创建线程的方式三: 实现Callable接口 ---JDK1.5新增
@since 2022/4/25 16:49
*/
public class ThreadNew {
public static void main(String[] args) {
NewCall newCall = new NewCall();
FutureTask task = new FutureTask(newCall);
new Thread(task).start();
//不需要返回值可省略
try {
Object sum = task.get();
System.out.println(sum);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class NewCall implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
八、线程的生命周期
JDK中用Thread.State类定义了线程的几种状态
-
==新建:==当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
-
==就绪:==处于新建状态的线程被start()后,将进入线程队列等待cpu时间片,此时它已具备了运行的条件,只是没分配到cpu资源
-
==运行:==当就绪的线程被调用并获得cpu资源时,便进入运行状态,run()方法定义了线程的操作和功能
-
==阻塞:==在某种情况下,被人为挂起或执行输入输出操作时,让出cpu并临时中止自己的执行,并进入阻塞状态
-
==死亡:==线程完成了它的全部工作,或线程被提前强制性地中止或出现异常导致结束
九、三种方法解决线程安全问题
当多个线程处理共享数据时会出现线程安全问题
方法一: 同步代码块
-
同步代码块解决了安全问题,但是操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程。效率低
-
同步监视器:锁
-
因为要确保多个线程必须要共用同一把锁
-
this 当前类对象
-
自定义类
-
当前类 : 面向对象 类也是对象
-
-
//类也是对象,类只加载一次,所以唯一
synchronized (Ticket1.class){}
//Class cla = Ticket1.class
/**
* synchronized (同步监视器){
* //需要同步的代码
* }
* 说明:1.操作共享数据的代码,即为需要同步的代码
* 2.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁
* 要求:多个线程必须要共用同一把锁
*/
public class TicketTest1 {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
new Thread(ticket1,"线程一").start();
new Thread(ticket1,"线程二").start();
new Thread(ticket1,"线程三").start();
}
}
class Ticket1 implements Runnable{
Object obj = new Object();
private int ticket = 100;
@Override
public void run() {
while(true){
synchronized (obj){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"获取票::"+ticket);
ticket--;
}else {
break;
}
}
}
}
}
//说明: 因为多个线程必须要共用同一把锁,所以在继承Thread方式实现的多线程时,锁必须保持唯一
private static Object obj = new Object(); //使用static来保证锁唯一
方法二:同步方法
- 实现Runnable接口的线程 synchronized的锁是this
- 继承Thread类的线程synchronized的锁是 类.class
class Ticket extends MyThread{
private static int ticket = 100;
@Override
public void run() {
while (true){
show();
}
}
/**
* 这里用静态修饰 就是为了确保锁对象唯一
* 这里的锁就是 Ticket.class
*/
private static synchronized void show(){
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"票::"+ticket);
ticket--;
}
}
}
方法三:Lock( 锁)(jdk5.0出现)
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
// 保证线程安全的代码;
}
finally{
lock.unlock();
}}}
class Window implements Runnable{
private int tecket=100;
private final ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while(true){
try{
lock.lock();
if(tecket>0)
{
try {//卖票慢点
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":售票,票号为:"+tecket);
tecket--;
}else{
break;
}
}finally{
lock.unlock();
}
}
}
}
对比:
1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
2. Lock只有代码块锁,synchronized有代码块锁和方法锁
3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
十、死锁问题
死锁
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
- 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
解决方法
- 专门的算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
/**
* 1.死锁的理解,不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁
* 2.出现死锁后,不会出现异常,不会出现提示,只是线程处于阻塞状态
*/
public class DeadThreadTest {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@SneakyThrows
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
sleep(1000);
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1+"::"+s2);
}
}
}
}.start();
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
synchronized (s2){
s1.append("a");
s2.append("1");
Thread.sleep(1000);
synchronized (s1){
s1.append("b");
s2.append("2");
System.out.println(s1+"::"+s2);
}
}
}
}).start();
}
}
十一、线程通信
-
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
-
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级别高的
-
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
注意:
- 1.wait() notify() notifyAll()三个方法必须使用在同步代码块或同步方法中
- 2.wait() notify() notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会报错IllegalMonitorStateException
- 3.wait() notify() notifyAll()三个方法是定义在java.lang.Object类中
-
/** * 线程通信例子:使用两个线程打印1-100.线程1,线程2交替打印 * * wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器 * notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级别高的 * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程 * * 说明:
-
1.wait() notify() notifyAll()三个方法必须使用在同步代码块或同步方法中
-
2.wait() notify() notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会报错IllegalMonitorStateException
- 3.wait() notify() notifyAll()三个方法是定义在java.lang.Object类中
*/
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number, "线程1");
Thread t2 = new Thread(number, "线程2");
t1.start();
t2.start();
}
}
class Number implements Runnable{
Object obj = new Object();
private int num = 1;
@Override
public void run() {
while (true){
synchronized (obj){
//唤醒
obj.notify();
if (num <= 100){
System.out.println(Thread.currentThread().getName()+":::"+num);
num++;
try {
//使用wait方法,让线程进去阻塞状态
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
十二、线程通信的应用:生产者/消费者
* /**
* 生产者生产产品交给店员,消费者从店员处取走产品,店员一次最多持有产品20,多了就让生产暂停,消费者消费时如果没产品也暂停,知道有产品在来取
*/
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
Thread p = new Thread(producer, "生产者");
Thread c = new Thread(consumer, "消费者");
p.start();
c.start();
}
}
//店员
class Clerk {
private int pro = 0;
//生产产品
public synchronized void produceProduct() {
if (pro < 20) {
pro++;
System.out.println(Thread.currentThread().getName() + "开始生产第" + pro + "件产品");
//唤醒消费
notify();
} else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (pro > 0) {
System.out.println(Thread.currentThread().getName() + "开始消费第" + pro + "件产品");
pro--;
//唤醒生产
notify();
} else {
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者
class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始生产产品");
while (true) {
clerk.produceProduct();
}
}
}
//消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始消费产品");
while (true) {
clerk.consumeProduct();
}
}
}
十三、sleep() 和 wait() 区别
-
相同点:都可以让线程进入阻塞状态
-
不同点:
-
两个方法声明位置不同:Thread中声明sleep(),Object类中声明wait()
-
调用的要求不同: sleep()可以在任何需要的场景下使用, wait()必须使用在同步代码块或同步方法中
-
如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()释放锁
-
十四、线程池
**背景:**经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
**思路:**提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可避免频繁创建销毁,实现重复利用。
好处:
-
提高响应速度(减少了创建新线程的时间)
-
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
-
便于线程管理
-
corePoolSize:核心池的大小
-
maximumPoolSize:最大线程数
-
keepAliveTime:线程没有任务时最多保持多长时间后会终止
-
线程池相关API:
JDK5.0起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task):执行任务,又返回值,一般用来执行Callable
- void shutdown():关闭连接池
Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
- Executors.newCachedThreadPool():创建一个可以根据需要创建新线程的线程池
- Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池 (定长)
- Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池 (一个)
- Executors.newScheduledThreadPool(n):创建一个线程池,它可安排给定延迟后运行命令或者定期地执行
使用Executors创建线程池 例: (不允许使用Executors创建线程池)
- 会导致OOM(内存溢出)
- 不允许原因:https://blog.csdn.net/ARPOSPF/article/details/120276045
public class ThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor service =(ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//设置线程池属性
// service.setCorePoolSize(10);
// service.setMaximumPoolSize(10);
//适合适用于Runnable
service.execute(new Number());
//适合适用于Callable
//service.submit();
//关闭连接池
service.shutdown();
}
}
class Number implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 ==0 ){
System.out.println(Thread.currentThread().getName()+"::"+i);
}
}
}
}
使用ThreadPoolExecutor创建线程池: 推荐使用
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
/**
* public ThreadPoolExecutor(int corePoolSize, //核心线程数
* int maximumPoolSize, //最大线程数
* long keepAliveTime, //最大空闲时间
* TimeUnit unit, //时间单位
* BlockingQueue<Runnable> workQueue, //阻塞队列
* ThreadFactory threadFactory, //线程工厂
* RejectedExecutionHandler handler) //拒绝策略
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
4,
10,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(2),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("adadada");
return thread;
}
},
new ThreadPoolExecutor.AbortPolicy());
//执行任务
executor.execute(()->{
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
});
//关闭线程池
executor.shutdown();
}
}
ThreadPoolExecutor
七个参数:
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //最大空闲时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) //拒绝策略
线程池执行流程:
线程池属性标识:
//是一个int类型的数值,表达两个意思,1:声明当前线程池的状态, 2:声明线程池中的线程数
//高3位是:线程池状态 低29位是:线程池中的线程个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //29 方便后面做位运算
private static final int CAPACITY = (1 << COUNT_BITS) - 1; //通过位运算得出最大容量
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; //111 代表线程池位RUNNING,代表正常接收任务
private static final int SHUTDOWN = 0 << COUNT_BITS; //000 代表线程池位SHUTDOWN状态,不接受新任务,但是内部还会处理阻塞队列中的任务,正在进行的任务也正常处理
private static final int STOP = 1 << COUNT_BITS; //001 代表线程池位STOP状态,不接受新任务,也不去处理阻塞队列中的任务,同时会中断正在执行的任务
private static final int TIDYING = 2 << COUNT_BITS; //010 代表线程池为TIDYING状态,过渡的状态,代表当前线程池即将Game Over
private static final int TERMINATED = 3 << COUNT_BITS; //011 代表线程池为TERMINATED状态,线程已经凉凉了
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; } //得到线程池的状态
private static int workerCountOf(int c) { return c & CAPACITY; } //得到当前线程池的线程数量(正在工作的线程数)
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池状态变化:
解析:
1、从execute方法开始: (配合上访线程流程图)
public void execute(Runnable command) {
//健壮性判断
if (command == null)
throw new NullPointerException();
//拿到32位int AtomicInteger ctl
int c = ctl.get();
//获取 工作线程数 < 核心线程数
if (workerCountOf(c) < corePoolSize) {
//进入if代表可创建 核心 线程数
if (addWorker(command, true))
//结束
return;
//如果if没进去,代表创建核心线程数失败,重新获取ctl 并发多线程原因造成
c = ctl.get();
}
//判断线程是不是RUNNING ,将任务添加到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
// 获取ctl
int recheck = ctl.get();
//再次判断线程是不是RUNNING ,如果不是,移除任务
if (! isRunning(recheck) && remove(command))
reject(command); //拒绝策略
else if (workerCountOf(recheck) == 0) //如果线程是RUNNING,BUT工作线程为0
addWorker(null, false); //阻塞队列有任务,但是没有工作线程,添加一个任务为空的工作线程处理阻塞队列中的任务 避免阻塞队列中的任务没有线程处理
}
// 阻塞队列满了 , 创建非核心线程任务
else if (!addWorker(command, false))
//创建非核心线程任务失败,执行拒绝策略
reject(command);
}
2、addWorker:
RUNNING都有可能,private boolean addWorker(Runnable firstTask, boolean core) {
retry: //for循环标记,可让内部循环跳出外部循环 break retry;
//for循环目的 经过大量的判断,给工作线程数标识+1
for (;;) {
//获取ctl
int c = ctl.get();
//获取线程池状态
int rs = runStateOf(c);
if (rs >= SHUTDOWN && // 除了RUNNING都有可能
! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())
//rs==SHUTDOWN 如果不是SHUTDOWN,就代表stop或者更高的状态,这时不需要天剑线程处理任务
//任务为空 如果任务为null,并且线程不为RUNNING,不需要处理
//阻塞队列不为null 如果阻塞队列为空,返回false.外侧!再次取反,获取true,不需要处理
)
//构建工作线程失败
return false;
for (;;) {
//获取工作线程个数
int wc = workerCountOf(c);
if (wc >= CAPACITY || //如果当前线程已经大于线程池最大容量,不用创建
wc >= (core ? corePoolSize : maximumPoolSize)) //判断wc是否超过核心线程或者最大线程
//构建工作线程失败
return false;
//将工作线程数+1,采用CAS的方式 保证数据原子性
if (compareAndIncrementWorkerCount(c))
//成功就退出外侧循环for
break retry;
//重新获取ctl
c = ctl.get();
//重新判断线程池状态,如果有变化, 如果状态没变,重新执行内部循环
if (runStateOf(c) != rs)
//结束这次外侧循环,开始下次外侧循环
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//work开始 = false
boolean workerStarted = false;
//work添加=false
boolean workerA0dded = false;
//worker就是工作线程
Worker w = null;
try {
//创建Worker,传入任务
w = new Worker(firstTask);
//从Worker中获取线程t
final Thread t = w.thread;
//如果线程t不为null
if (t != null) {
//获取线程池的全局锁,避免我添加任务时,其他线程干掉了线程池,干掉线程池需要先获取这个锁
final ReentrantLock mainLock = this.mainLock;
//开启锁
mainLock.lock();
try {
//获取线程池状态
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || //是RUNNING状态
(rs == SHUTDOWN && firstTask == null)) { //是SHUTDOWN状态,创建空任务工作线程,处理阻塞队列中的任务
if (t.isAlive()) // 线程是否运行状态
throw new IllegalThreadStateException();
//将工作线程添加到集合中
workers.add(w);
//获取工作线程个数
int s = workers.size();
//如果线程工作数,大于之前记录的最大工作线程数,就替换一下
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true; //添加工作线程成功
}
} finally {
//释放锁
mainLock.unlock();
}
if (workerAdded) {
//启动工作线程
t.start();
workerStarted = true;//启动工作线程成功
}
}
} finally {
if (! workerStarted) //如果启动工作线程失败
addWorkerFailed(w);
}
return workerStarted; //返回工作线程是否启动
}
3、Worker:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
//由Worker构造可得 调用的start方法走的是 Worker中的Run方法,也就是运行runWorker
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
4、runWorker:
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//拿到任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
//标识为true
boolean completedAbruptly = true;
try {
//任务不空,执行任务 如果任务为空,通过getTask从阻塞队列中获取任务
while (task != null || (task = getTask()) != null) {
w.lock(); //加锁 避免你SHUTDOWN我任务也不会中断,获取不到锁
//获取当前状态,是否大于等于STOP, 线程凉了,或快凉了
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
//中断线程
wt.interrupt();
try {
//前置操作
beforeExecute(wt, task);
Throwable thrown = null;
try {
//调用
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//后置操作
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//线程执行完毕后续处理
processWorkerExit(w, completedAbruptly);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/194950.html