线程基础学习

线程的实现

  1. 通过实现Runnable接口的方式,实现其中的run方法。
  2. 继承Thread类,然后重写其中的run方法。
  3. 通过线程池创建线程,默认采用DefaultThreadFactory。
  4. 有返回值的callable,实现callable接口,实行call方法。

本质上,都是通过1,2两种类型创建方式

实现runnable接口要比继承thread类更好

好处:

  1. 可以把不同的内容进行解耦,权责分明。
  2. 某些情况下可以提升性能,减小开销。
  3. 继承Thread类相当于限制了代码未来的可拓展性。

如何正确停止线程

启动线程需要调用Thread类的start()方法,并且在run()方法中定义需要执行的任务。

使用interrupt停止线程,这个是属于通知接口,是否真的暂停取决于线程本身。

java 复制代码
public class StopThread implements Runnable {
    @Override
    public void run() {
        int count = 0;
        // 判断线程释放被中断,并且判断count是否小于1000
        while (!Thread.currentThread().isInterrupted() && count < 1000) {
            System.out.println("count = " + count++);
            // 休眠50秒钟
            Thread.sleep(50000);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThread());
        thread.start();
        // 休眠5秒钟
        Thread.sleep(5000);
        // 终端线程
        thread.interrupt();
    }
}

如果在第一段代码中休眠的时候,第二段代码线程发起中断,则休眠线程则会java.lang.InterruptedException: sleep interrupted 异常中断,抛出异常。

错误的停止方法

  • stop()会把线程停止,导致任务戛然而止有风险
  • suspend()容易导致死锁,因为线程A调用suspend()让B挂起,B线程没有释放锁就进入休眠,如果A想要拿到B持有的锁,这是B在休眠就导致A拿不到锁就死锁了。
  • resume()

使用volatile标记为的停止方法是可能出现问题

java 复制代码
public class StopThread implements Runnable {
    // volatile标记位
    public volatile boolean canceled = false;
    @Override
    public void run() {
        int count = 0;
        // 判断线程释放被中断,并且判断count是否小于1000
        while (!canceled && !Thread.currentThread().isInterrupted() && count < 1000) {
            System.out.println("count = " + count++);
            // 休眠50秒钟
            Thread.sleep(50000);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThread());
        thread.start();
        // 休眠5秒钟
        Thread.sleep(5000);
        // 终端线程
        thread.interrupt();
        thread.canceled = true;
    }
}

线程在6种状态之间转化

线程的生命周期

  1. New(新创建)表示线程被创建但尚未启动的状态,当线程调用start(),就变成Runnable。
  2. Runnable(可运行)表示java中的线程可以running或者是ready状态
  3. Blocked(被阻塞)

从runnable状态转到Blocked状态,只有一种可能性,就是进入synchronized保护的代码时没有抢到monitor锁。

转化成runnable状态:获取到monitor锁

  1. Waiting(等待)

进入waiting状态的三种可能性

a. 没有设置Timeout参数的Object.wait()方法

b. 没有设置Timeout参数的Thread.join()方法

c. LockSupport.park()方法

wait()会释放monitor锁

唤醒:如果其他线程调用notify()或者notifyAll()来唤醒它,会直接进入到Blocked状态。因为唤醒Waiting线程的线程如果调用notify()或者notifyAll(),要求必须首先持有该monitor锁,所以处于waiting状态的线程被唤醒时拿不到该锁,会进入blocked状态

  1. Timed_waiting(计时等待)

跟waiting的区别是时间到了就自动唤醒或者是等待唤醒信号进行唤醒

  1. Terminated(被终止)

wait/notify/notifyAll方法的使用注意事项

为什么wait必须在synchronized保护的同步代码中使用?

java 复制代码
/**
 * 在使用wait()方法时,必须将wait方法写在synchronized保护的while代码块中,并始终判断条件是否满足
 * 如果满足就往下执行,如果不满足就执行wait()方法,在执行wait()方法之前,必须持有对象的monitor锁,也就synchronized锁
 */
public void give(String data) {
    synchronized (this) {
        buffer.add(data);
        notify();
    }
}

public String take() throws InterruptedException {
    synchronized (this) {
        while (buffer.isEmpty()) {
            wait();
        }
        return buffer.remove();
    }
}

为什么wait/notify/notifyAll方法被定义在Object类中,而sleep定义在Thread类中?

因为Java中每个对象都有一把称之为monitor监视器的锁,由于每个对象都可以上锁,这就要求在对象头中有一个用来保存锁信息的位置,这个锁是对象级别的,而非线程级别的,wait/notify/notifyAll也都是锁级别的操作,它们的锁属于对象。

所以把它们定义在Object类中是最合适,因为Object类是所有对象的父类

wait/notify和sleep方法的异同

相同点:

  1. 都可以让线程阻塞
  2. 它们都可以响应interrupt中断:在等待的过程中如果收到中断信号,都可以进行响应,

并抛出InterruptedException异常

不同点:

  1. wait方法必须在synchronized保护的代码中使用,而sleep方法并没有这个要求
  2. 在同步代码中执行sleep方法时,并不会释放monitor锁,但执行wait方法时会主动释放monitor锁
  3. sleep方法中会要求必须定义一个时间,时间到期后会主动恢复,而对于没有参数的wait方法而言,意味着永久等待,直到被中断或被唤醒才能恢复,它并不会主动恢复
  4. wait/notify是Object类的方法,而sleep是Thread类的方法
相关推荐
Scott9999HH13 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔13 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社14 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海14 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖15 小时前
JDK 26 新特性详解
java·开发语言
马优晨15 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a11177617 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao17 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
人邮异步社区17 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大18 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf