线程基础学习

线程的实现

  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类的方法
相关推荐
AlexMercer10129 分钟前
[C++ 核心编程]笔记 4.2.6 初始化列表
开发语言·数据结构·c++·笔记·算法
lifejump10 分钟前
基于PHP的http字段查询与注册(V1)(持续迭代)
开发语言·php
懒惰才能让科技进步17 分钟前
从零学习大模型(十)-----剪枝基本概念
人工智能·深度学习·学习·语言模型·chatgpt·gpt-3·剪枝
程序员阿鹏18 分钟前
详解:模板设计模式
java·开发语言·jvm·后端·设计模式·eclipse·1024程序员节
wjs202418 分钟前
Lua 函数
开发语言
何苏三月20 分钟前
设计模式 - 简单工厂模式
java·设计模式·简单工厂模式
源于花海34 分钟前
论文学习 | 《锂离子电池健康状态估计及剩余寿命预测研究》
论文阅读·人工智能·学习·论文笔记
懒惰才能让科技进步34 分钟前
从零学习大模型(八)-----P-Tuning(上)
人工智能·pytorch·python·深度学习·学习·自然语言处理·transformer
weixin_3784102439 分钟前
java springboot项目如何计算经纬度在围栏内以及坐标点距离
java·开发语言·spring boot
霍格沃兹测试开发学社测试人社区42 分钟前
软件测试学习笔记丨Selenium学习笔记:css定位
软件测试·笔记·测试开发·学习·selenium