JUC并发编程(二)

一、过时方法

一些不推荐使用的方法已经过时,容易破坏同步代码块,使对象的锁得不到释放,进而造成线程死锁
在这里插入图片描述

二、守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

正常情况下:主线程运行结束,但t1线程仍未结束,因此进程并不会结束(只要有一个线程还在运行,Java进程并不会结束
在这里插入图片描述

将t1设置为守护线程后

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test15")
public class Test15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    break;
                }
            }
            log.debug("结束");
        }, "t1");
        // 将t1设置为守护线程(默认值为非守护线程:false)
        t1.setDaemon(true);   
        t1.start();

        Thread.sleep(1000);
        log.debug("结束");
    }
}

运行结果:主线程(非守护)运行结束后,无论守护线程(t1)是否执行完毕均会强制结束
在这里插入图片描述

守护线程应用
● 垃圾回收器线程就是一种守护线程【再堆内存中分配的对象,当没有其它对象引用这些对象时,这些未被引用的对象就会定期被垃圾回收
● Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

三、线程状态(五种)

五种状态,这是从 操作系统 层面来描述的
在这里插入图片描述
初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联(egJava中newThread对象,但未调用其start()方法

● 【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行,但暂时还未获得CPU的时间片

● 【运行状态】指获取了 CPU 时间片运行中的状态
—— 当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,其切换会导致线程的上下文切换

● 【阻塞状态
—— 如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU(调度器不会考虑调度阻塞状态的线程),会导致线程上下文切换,进入【阻塞状态】
—— 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
—— 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们

● 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

四、线程状态(六种)

六种状态,是从Java API层面来描述的

根据Thread.State枚举,分为六种状态
在这里插入图片描述
NEW 线程刚被创建,但是还没有调用 start() 方法

RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)操作系统层面的阻塞状态在Java中还是RUNNABLE

BLOCKED WAITINGTIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分,后面会在状态转换一节详述

TERMINATED 当线程代码运行结束

3.1 六种状态演示

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

@Slf4j(topic = "c.TestState")
public class TestState {
    public static void main(String[] args) throws IOException {

        // ① 并未调用线程1的start()方法===>NEW
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };

        // ② 不断运行===>RUNNABLE(既有可能分到时间片也有可能为分到时间片,也有可能陷入操作系统的IO阻塞)
        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                while(true) { // runnable

                }
            }
        };
        t2.start();

        // ③ 打印后执行完毕===>TERMINATED
        Thread t3 = new Thread("t3") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };
        t3.start();

        // ④ 线程处于阻塞状态【sleep时间足够长===>TIMED_WAITING(有时限的的等待)】
        Thread t4 = new Thread("t4") {
            @Override
            public void run() {
                synchronized (TestState.class) {
                    try {
                        Thread.sleep(1000000); // timed_waiting
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t4.start();

        // ⑤ 等待t2(死循环)一直运行结束===>WAITING(没有时限的等待)
        Thread t5 = new Thread("t5") {
            @Override
            public void run() {
                try {
                    t2.join(); // waiting
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t5.start();

        // ⑥ t4线程会先对对象加锁,t6线程不能获取对象的锁===>t6陷入BLOCKED
        Thread t6 = new Thread("t6") {
            @Override
            public void run() {
                synchronized (TestState.class) { // blocked
                    try {
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t6.start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("t1 state {}", t1.getState());
        log.debug("t2 state {}", t2.getState());
        log.debug("t3 state {}", t3.getState());
        log.debug("t4 state {}", t4.getState());
        log.debug("t5 state {}", t5.getState());
        log.debug("t6 state {}", t6.getState());
        System.in.read();
    }
}

运行结果:
在这里插入图片描述

四、多线程应用统筹分析

如何使用多线程得到最优方案?
在这里插入图片描述
后两种办法均耗费时间较长
在这里插入图片描述
上图可以一眼看出,办法甲总共要16分钟(而办法乙、丙需要20分钟)。因此合理分配便可节约大量时间,我们可以使用多线程的思想便可找到最优的方案
在这里插入图片描述
方案实现:

import lombok.extern.slf4j.Slf4j;

import static cn.itcast.n2.util.Sleeper.sleep;

@Slf4j(topic = "c.Test16")
public class Test16 {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("洗水壶");
            sleep(1);
            log.debug("烧开水");
            sleep(5);
        },"老王");

        Thread t2 = new Thread(() -> {
            log.debug("洗茶壶");
            sleep(1);
            log.debug("洗茶杯");
            sleep(2);
            log.debug("拿茶叶");
            sleep(1);
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("泡茶");
        },"小王");

        t1.start();
        t2.start();
    }
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

new一个对象_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值