线程基础学习

news2024/10/24 23:17:44

线程的实现

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

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

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

好处:

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

如何正确停止线程

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

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

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标记为的停止方法是可能出现问题

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保护的同步代码中使用?

/**
 * 在使用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类的方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2210994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

wait和waitpid

在Linux中&#xff0c;wait 和 waitpid 是用于进程控制的系统调用&#xff0c;它们主要用来让父进程等待子进程的终止&#xff0c;并获取子进程的退出状态。下面详细介绍它们的用法和区别。 1. wait() 函数 wait() 会阻塞调用进程&#xff0c;直到一个子进程终止。它的典型用…

怎么ping网络ip地址通不通

怎么Ping网络IP地址通不通&#xff1f;要检查网络中的IP地址是否连通&#xff0c;可以使用‌Ping命令。Ping命令通过发送ICMP&#xff08;Internet Control Message Protocol&#xff0c;因特网控制消息协议&#xff09;Echo请求报文并等待回应&#xff0c;来判断目标主机是否可…

ARP限制网速攻击

ARP限制网速攻击 大家没想到吧&#xff0c;ARP还能限制对方网速。当kali欺骗了网关和受害者的时候&#xff0c;受害者访问网络就需要经过kali的网卡&#xff0c;那我们限制kali网卡的速度或者转发的速度就可以限制对方的网速。这里可以使用的工具有tc、iptables、WonderShaper…

Unix Standardization and Implementations

Unix标准化 在Unix未制定较为完备的标准时&#xff0c;各个平台的系统调用方式各异&#xff0c;所开发出的应用程序存在可移植性差的特点&#xff0c;因此人们呼吁指定一套Unix标准来规范接口&#xff0c;增加应用程序的可移植性。所谓Unix标准即适用于Unix环境下的一系列函数…

AI 时代技术盛宴 —— 稀土掘金 × 豆包 MarsCode 青训营等你来!

各位 CSDN 的小伙伴们&#xff0c;今天我要给大家带来一个超级棒的消息&#xff01;稀土掘金与豆包 MarsCode 强强联手&#xff0c;共同打造的青训营再次上线啦&#xff01; 在这个飞速发展的 AI 时代&#xff0c;这场青训营玩出了不一样的精彩。它携手 AI 伙伴豆包 MarsCode&…

一个项目用5款数据库?MySQL、PostgreSQL、ClickHouse、MongoDB区别,适用场景

文章目录 一、常用数据库概览1.1 关系型数据库1.2 非关系型数据库1.2.1 KV数据库1.2.2 文档型数据库1.2.3 列式存储数据库1.2.4 图数据库 1.3 SQL与NoSQL区别1.3.1 结构化与非结构化1.3.2 关联和非关联1.3.3 查询方式1.3.4 事务1.3.5 总结 二、MySQL三、PostgreSQL3.1 特点、适…

计组_中断响应条件

2024.10.13&#xff1a;计算机组成原理学习笔记 中断响应条件 条件1&#xff1a;CPU要处于开中断状态条件2&#xff1a;至少要有一个未被屏蔽的中断请求屏蔽&#xff1f;中断优先级屏蔽字的作用 条件3&#xff1a;当前指令刚执行完 条件1&#xff1a;CPU要处于开中断状态 当CP…

Redis:分布式 - 主从复制

Redis&#xff1a;分布式 - 主从复制 概念配置主从模式info replicationslave-read-onlytcp-nodelay 命令slaveof 主从结构一主一从一主多从 主从复制流程数据同步命令全量同步部分同步实时同步 节点晋升 概念 Redis的最佳应用&#xff0c;还是要在分布式系统中。对于非分布式…

如何解决 Vim 中的 “E212: Can‘t open file for writing“ 错误:从编辑到权限管理(sudo)

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

Squad战术小队开服教程

1、登录服务器&#xff08;百度莱卡云游戏面板&#xff09; 登录面板的信息在绿色的登陆面板按键下方&#xff0c;不是你的莱卡云账号 进入控制面板后会出现正在安装的界面&#xff0c;大约10分钟左右就能安装完成 2、设置 点击目录上的网络&#xff0c;你可以看到三个端口 然…

【Python】Twisted:让自定义网络应用开发变得简单

Twisted 是 Python 中一个成熟、功能强大的事件驱动网络编程框架。它支持多种协议&#xff0c;如 HTTP、FTP、SMTP 等&#xff0c;可以用于构建高性能的网络应用&#xff0c;包括网络服务器、客户端、代理服务器等。Twisted 的核心是基于事件驱动模型&#xff0c;能够有效处理大…

el-date-picker设置只有某些日期可选

示例图&#xff1a; <el-date-pickerv-model"topFormObj.upTime"type"date"value-format"timestamp"format"dd/MM/yyyy":picker-options"pickerOptions" /> 固定限制每周的周末周三不可选 data() {return {pickerOp…

Leetcode 乘积最大子数组

该算法的目的是求出一个整数数组中乘积最大的连续子数组。 算法思想&#xff1a; 问题分析&#xff1a; 我们需要在数组中找到连续的子数组&#xff0c;使得该子数组的乘积最大。这个问题类似于“最大子序和”&#xff0c;但乘积相比求和有更多的复杂性&#xff0c;特别是当数…

销售管理的五大职能

什么是销售管理 销售管理&#xff0c;这一看似简单的概念&#xff0c;实则蕴含着丰富的内涵与细致的操作。它不仅仅是对销售团队的监督和对客户满意度的追求&#xff0c;更是通过一系列策略和手段&#xff0c;优化销售流程&#xff0c;提升业务成果的过程。 销售管理的内核 …

electron本地OCR实现

使用tesseract.js - npm (npmjs.com) 官方demo&#xff1a;GitHub - Balearica/tesseract.js-electron: An example to use tesseract.js in electron 目录结构&#xff1a; async function ocr() {const worker await Tesseract.createWorker("chi_sim", 1, {wor…

前端打印功能(vue +springboot)

后端 后端依赖生成pdf的方法pdf转图片使用(用的打印模版是带参数的 ,参数是aaa)总结 前端页面 效果 后端 依赖 依赖 一个是用模版生成对应的pdf,一个是用来将pdf转成图片需要的 <!--打印的--><dependency><groupId>net.sf.jasperreports</groupId>&l…

探索 YOLO11:更快、更智能、更高效

点击下方卡片&#xff0c;关注“小白玩转Python”公众号 在人工智能这个不断进化的世界中&#xff0c;有一件事我们可以肯定&#xff1a;模型不断变得更好、更快、更智能。就在你以为 YOLO 系列已经达到顶峰时&#xff0c;Ultralytics 推出了最新升级——YOLO11。没错&#xff…

K-means 聚类算法:目标函数推导、迭代过程及可视化解析

一、K-means 的背景 在机器学习领域&#xff0c;许多任务涉及 训练模型来做预测或分类 。比如&#xff0c;医生可能希望通过以往的病例数据来预测某个病人未来是否会患上某种疾病&#xff0c;或者新闻网站可能需要根据文章的主题将新闻自动分类。这些任务通常依赖于有标签的数…

Qt之TCP收发图片的例子

一.效果 二.实现 1.发图片 void MainWindow::slotSendImage() {matrix.rotate(90);QPixmap tempPixmap = pixmap.transformed(matrix);QBuffer buffer;tempPixmap.save(&buffer,"jpg");ui->labelImage->setPixmap(tempPixmap);int dataLength = buffer.d…

UE4 材质学习笔记09(雨水水坑着色器/完整雨水着色器)

一.雨水水坑着色器 要用到这样一个噪声贴图&#xff0c;我们要做的就是&#xff0c;做出水坑并让水坑在这种浑浊的噪点中产生&#xff0c;因此水坑将从最暗的斑点生长&#xff0c;然后随着它继续占据越来越亮的像素而生长 现在水坑将从上到下投射到世界空间中&#xff0c;所以…