线程基础学习

线程的实现

  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类的方法
相关推荐
XH华1 小时前
C语言第十一章内存在数据中的存储
c语言·开发语言
叫我阿柒啊3 小时前
Java全栈开发面试实战:从基础到微服务架构
java·vue.js·spring boot·redis·git·full stack·interview
小凡敲代码3 小时前
2025年金九银十Java面试场景题大全:高频考点+深度解析+实战方案
java·程序员·java面试·后端开发·求职面试·java场景题·金九银十
AndrewHZ3 小时前
【python与生活】如何用Python写一个简单的自动整理文件的脚本?
开发语言·python·生活·脚本·文件整理
拉法豆粉3 小时前
在压力测试中如何确定合适的并发用户数?
java·开发语言
枯萎穿心攻击4 小时前
Unity VS UE 性能工具与内存管理
开发语言·游戏·unity·ue5·游戏引擎·虚幻·虚幻引擎
red_redemption4 小时前
自由学习记录(88)
学习
爱上纯净的蓝天4 小时前
迁移面试题
java·网络·c++·pdf·c#
老赵的博客4 小时前
c++ 常用接口设计
开发语言·c++
binbinaijishu884 小时前
Python爬虫入门指南:从零开始的网络数据获取之旅
开发语言·爬虫·python·其他