线程的基本方法

news2024/11/16 9:39:20

1111111

线程等待:wait方法

  • 调用wait方法的线程会进入WAITING状态,只有等到其他线程的通知程序被中断才会返回。
  • 调用wait方法后会释放对象的锁,因此 wait方法一般被用于同步方法或同步代码块中

线程睡眠:sleep方法

  • 调用sleep方法会导致当前线程休眠。 sleep方法暂停指定的时间,让出CPU给其他线程,但其监控状态依然保持,在指定的时间过后又会自动恢复运行状态。
  • sleep方法不会释放当前占有的锁,会导致线程进入TIMED-WATING状态。

sleep() 和wait() 有什么区别?

参考 :sleep()和wait()区别.

  • 所属的类

sleep属于Thread类静态方法,wait则属于Object类;

  • sleep自己会唤醒,wait需要被唤醒
  • sleep 必须指定时间,wait不必须指定时间
  • 线程状态

sleep会导致线程进入TIMED-WATING状态,而wait方法会导致当前线程进入WATING状态。

  • 适用范围

wait,notify,notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以任何地方使用

  • 都需要异常处理
  • sleep哪个线程调用哪个线程等待,wait会使当前拥有该对象锁的线程等待
  • sleep释放CPU执行权不释放同步锁,wait释放CPU执行权也释放同步锁
  • sleep通常被用于暂停执行,wait通常被用于线程间交互

线程让步:yield方法

调用yield方法会使当前线程让出(释放)CPU执行时间片,与其他线程一起重新竞争CPU时间片

在一般情况下,优先级高的线程更有可能竞争到CPU时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。

线程中断:interrupt方法

interrupt方法用于向线程发行一个终止通知信号,会改变该线程内部的一个中断标识位,线程本身并不会因为调用了interrupt方法而改变状态(阻塞、终止等),状态的具体变化需要等待接收到中断标识的程序的最终处理结果来判定。
对interrupt方法的理解需要注意以下4个核心点。

  • 调用interrupt方法并不会中断一个正在运行的线程,也就是说处于Running状态的线程并不会因为被中断而终止,仅仅改变了内部维护的中断标识位而已;
  • 若因为调用sleep方法而使线程处于TIMED-WATING状态,则这时调用interrupt方法会抛出InterruptedException,使线程提前结束TIMED-WATING状态。
  • 许多声明抛出InterruptedException的方法如Thread.sleep(long mills),在抛出异常前都会清除中断标识位,所以在抛出异常后调用isInterrupted方法将会返回false。
  • 中断状态是线程固有的一个标识位,可以通过此标识位安全终止线程。比如,处理线程结束前必要的一些资源释放和清理工作;比如:释放锁,存储数据到持久层,发出异常通知

线程加入:join方法

如果在当前线程中调用另一个线程的join方法,则当前线程转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取CPU的使用权。

通俗理解(让多个线程同步执行—>变成单线程)

如下有两个线程:

public class Join_OnlyOne extends Thread {
    public void run(){
        System.out.println("thread_one start");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread_one end");
    }
}

public class Join_OnlyTwo extends Thread {
    public void run() {
        System.out.println("thread_two start");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread_two end");
    }
}

        Join_OnlyOne one = new Join_OnlyOne();
        Join_OnlyTwo two = new Join_OnlyTwo();
        one.start();
        two.start();

如果这样写的话线程one和线程two是交替执行的,但是不一定谁先启动

如何设置顺序?

可以设置 two线程要在one的run里面创建-----------这样保证两个有先后顺序

public class Join_OnlyTwo_In_One extends Thread {
    public void run() {
        System.out.println("thread_one start");
        Join_OnlyTwo two = new Join_OnlyTwo();
        two.start();
        System.out.println("thread_one End");
    }
}

    public static void main(String[] args) {
        Join_OnlyTwo_In_One one = new Join_OnlyTwo_In_One();
        one.start();
    }

thread_one start
thread_one End
thread_two start
thread_two end

以上只是保证,线程1比线程2先开始执行,后面就开始争抢资源不一定谁先结束

如何保证线程2执行完了,再执行线程1呢?

两个线程合并成一个线程
public class Join_OnlyTwo_Join_One extends Thread {
    public void run() {
        System.out.println("thread_one start");
        Join_OnlyTwo two = new Join_OnlyTwo();
        two.start();
        try {
            two.join();//线程2加入到线程1里, 线程2执行完才会执行线程1
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread_one end");
    }
}

thread_one start
thread_two start
thread_two end
thread_one end

这样就太给线程2特权了,线程2不执行完,线程1就一直等着!!!
还可以往回收收权利,给线程2特定时间,
表示:我只等你约定的时间,超过这时间,我要和你一起争夺资源了


//两个线程合并成一个线程
public class Join_OnlyTwo_Join_One extends Thread {
    public void run() {
        System.out.println("thread_one start");
        Join_OnlyTwo two = new Join_OnlyTwo();
        two.start();
        try {
            two.join(2000);//就等线程2 2000毫秒 超过就不等了
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread_one end");
    }
}

thread_one start
thread_two start
thread_one end
thread_two end
参看下面例子体会Join

public class Join_Lock_ThreadOne extends Thread {
    public void run(){
        System.out.println(Thread.currentThread().getName() +"   thread_one start:  "+ new Date());
        Join_Lock_ThreadTwo two = new Join_Lock_ThreadTwo();
        two.start();
        try {
            two.join(2000);//线程2加入到线程1里, 线程2先执行。但是线程1只会等线程2 2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +"   thread_one end:   "+ new Date());
    }
}
public class Join_Lock_ThreadTwo extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + "   thread_two start:   "+ new Date());
        Join_Lock_ThreadThree three = new Join_Lock_ThreadThree(this);
        three.start();
        try {
            Thread.sleep(5000);//线程2休眠 5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "   thread_two end:   "+ new Date());
    }
}
public class Join_Lock_ThreadThree extends Thread{
    private Join_Lock_ThreadTwo two;

    public Join_Lock_ThreadThree(Join_Lock_ThreadTwo two) {
        this.two = two;
    }

    public void run(){
        //在two执行过程中 one等待的过程中 three将two对象锁定
        System.out.println(Thread.currentThread().getName() + "   thread_three start:   "+ new Date());
        synchronized (two){
            System.out.println(Thread.currentThread().getName() + "   two is locked:"+ new Date());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "   two is free:   "+ new Date());
        }
        System.out.println(Thread.currentThread().getName() + "   thread_three end:   "+ new Date());
    }
}
public class Join_Lock_TestMain {
    public static void main(String[] args) {
        Join_Lock_ThreadOne one = new Join_Lock_ThreadOne();
        one.start();
    }
}

Thread-0   thread_one start:  Mon Sep 07 14:15:27 GMT+08:00 2020	线程one启动并执行,线程two启动并join到线程one里
Thread-1   thread_two start:   Mon Sep 07 14:15:27 GMT+08:00 2020	线程two执行,线程three启动,线程two休眠
Thread-2   thread_three start:   Mon Sep 07 14:15:27 GMT+08:00 2020	线程three启动并执行。
Thread-2   two is locked:Mon Sep 07 14:15:27 GMT+08:00 20202s(线程one join需要等2s)5s(线程two sleep 5s)之内都是线程three优先执行,执行代码锁定two对象10s
																	过了2s之后,(线程two sleep 5s),(线程three sleep 10s),线程one执行,线程one想把线程two从自己的线程内删除,但是发现two对象不在自己手里,而是被线程three锁定了,而且锁定了10s,线程one只能等待
																	(5s以后线程two才会醒,还有3秒,这3秒就不清楚线程one一直在等待,还是循环获取到执行权然后被拒绝的这种循环???????????????????????)
Thread-1   thread_two end:   Mon Sep 07 14:15:32 GMT+08:00 2020	    5s以后线程two醒了,(线程three sleep 10s),线程two和线程one争夺资源,线程one获得执行权也是被拒绝,线程two获取到执行权才会去执行;
Thread-2   two is free:   Mon Sep 07 14:15:37 GMT+08:00 2020	    (尽管过了2s之后线程one想把线程two从自己的线程内删除,但是发现two对象不在自己手里,而是被线程three锁定了,而且锁定了10s,线程one只能等待),线程one获得执行权也是被拒绝,一直挺到,(线程three sleep 10s)结束后线程three将two对象释放
Thread-0   thread_one end:   Mon Sep 07 14:15:37 GMT+08:00 2020	    此时线程one和线程three争夺资源,谁执行完全随机。如果线程one获取到执行权的话,线程one把线程two从自己的线程内删除, 对于最后的输出语句,依旧线程one和线程three争夺资源,最后这两步不一定谁先执行
Thread-2   thread_three end:   Mon Sep 07 14:15:37 GMT+08:00 2020	

以上例子可以得到:
synchronized锁非常的厉害, 一旦对象被锁定不释放的情况下,其他的对象都需要等待

线程唤醒:notify方法

Object类有个notify方法,用于唤醒在此对象监视器上等待的一个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的。 notifyAll,用于唤醒在监视器上等待的所有线程

后台守护线程:setDaemon方法

setDaemon方法用于定义一个守护线程,也叫作“服务线程”,该线程是后台线程; 将一个用户线程设置为守护线程的方法是在线程对象创建之前用线程对象的setDaemon(true)来设置。

在这里插入图片描述

终止线程的4种方式

1.正常运行结束

指线程体执行完成,线程自动结束。

2.使用退出标志退出线程

在一般情况下,在run方法执行完毕时,线程会正常结束。然而,有些线程是后台线程,需要长时间运行,只有在系统满足某些特殊条件后,才能触发关闭这些线程

public class volatile_ extends Thread{
    //volatile,这个关键字用于使exit线程同步安全,也就是说在同一时刻只能有一个线程修改exit的值,在exit为true时,while循环退出。
    public volatile  boolean exit = false;
    public void run (){
        while (!exit){
            //执行业务代码逻辑
        }
    }
}

3. 使用Interrupt方法终止线程

(1)线程处于阻塞状态。

例如,在使用了sleep、调用锁的wait或者调用socket的receiver、accept等方法时,会使线程处于阻塞状态。在调用线程的interrupt方法时,会抛出InterruptException异常。通常很多人认为只要调用interrupt方法就会结束线程,这实际上理解有误,一定要先捕获InterruptedException异常再通过break跳出循环,才能正常结束run方法。

(2)线程未处于阻塞状态。

此时,使用isInterrupted方法判断线程的中断标志来退出循环。在调用interrupt方法时,中断标志会被设置为true,并不能立刻退出线程,而是执行线程终止前的资源释放操作,等待资源释放完毕后退出该线程。

4.使用stop方法终止线程:不安全

在程序中可以直接调用Thread.stop方法强行终止线程,但这是很危险的,就像突然关闭计算机的电源,而不是正常关机一样,可能会产生不可预料的后果。在程序使用Thread.stop方法终止线程时,该线程的子线程会抛出ThreadDeatherror错误,并且释放子线程持有的所有锁。加锁的代码块一般被用于保护数据的一致性,如果在调用Thread.stop方法后导致该线程所持有的所有锁突然释放而使锁资源不可控制,被保护的数据就可能出现不一致的情况,其他线程在使用这些被破坏的数据时,有可能使程序运行错误。因此,并不推荐采用这种方法终止线程。

现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。(上面有实现的代码)

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

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

相关文章

Spring Boot 版本升级2.2.11.RELEASE至2.7.4

2.2.11.RELEASE > 2.7.4项目更新spring-boot-starter-parent 主依赖,导致项目跑不起了日志也没有输出有用信息,自己查看源码调试启动入口打断点,一步步进入方法定位项目停止代码我的项目执行到SpringApplication.class 的152行代码会停止项…

华为HCIE学习之Openstack Glance组件(glance对接swift)

文章目录一、Glance的结构二、服务部署流程三、将glance存储在swift中1、默认使用swift来存储2、指定可以存在swift中3、swift版本4、keystone的endpoint地址(当glance去找swift时通过keystone去找)5、租户名:用户名,用户必须拥有admin角色6、…

【C语言】自定义类型:结构体、枚举、联合

目录 1.结构体 1.1结构体类型 1.2结构体的自引用 1.3结构体的初始化 1.4结构体内存对齐 //对齐 //offsetof //修改默认对齐数 1.5结构体传参 2.位段 2.1位段的内存开辟 2.2位段的内存分配 3.枚举 4.联合(共用体) //判断大小端 1.结构体…

【GO】k8s 管理系统项目23[前端部分–工作负载-Pod]

k8s 管理系统项目[前端部分–工作负载-Deployment] 1. 代码部分 1.1 准备工作 由于Pod页面和Deployment内容差不多.那么就直接把Deployment的内容复制过来.再做修改. 替换Deployment为Pod替换Deploy为Pod替换deployment为pod替换deploy为pod禁用新增的按钮,删除新增方法,表…

django后端服务、logstash和flink接入VictoriaMetrics指标监控

0.简介 通过指标监控可以设置对应的告警,快速发现问题,并通过相应的指标定位问题。 背景:使用的 VictoriaMetrics(简称 VM) 作为监控的解决方案,需要将 django 服务、logstash 和 flink 引擎接入进来,VM 可以实时的获…

SpringBoot:SpringBoot配置文件.properties、.yml 和 .ymal(2)

SpringBoot配置文件1. 配置文件格式1.1 application.properties配置文件1.2 application.yml配置文件1.3 application.yaml配置文件1.4 三种配置文件优先级和区别2. yaml格式2.1 语法规则2.2 yaml书写2.2.1 字面量:单个的、不可拆分的值2.2.2 数组:一组按…

操作系统权限提升(十八)之Linux提权-内核提权

Linux 内核提权 Linux 内核提权原理 内核提权是利用Linux内核的漏洞进行提权的,内核漏洞进行提权一般包括三个环节: 1、对目标系统进行信息收集,获取到系统内核信息及版本信息; 2、根据内核版本获取其对应的漏洞以及EXP 3、使…

第七届蓝桥杯省赛 C++ A/B组 - 四平方和

✍个人博客:https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 📚专栏地址:蓝桥杯题解集合 📝原题地址:四平方和 📣专栏定位:为想参加蓝桥杯的小伙伴整理常考算法题解,祝大家…

Docker简介与用法

文章目录1、Docker简介1.1、Docker能解决什么问题1.2、什么是虚拟机技术1.2.1、虚拟机的缺点1.3、什么是容器1.3.1、容器与虚拟机比较1.4、分析 Docker 容器架构1.4.1、Docker客户端和服务器1.4.2、Docker 镜像(Image)1.4.3、Docker 容器(Container)1.4.4、Docker 仓库(reposit…

Windows程序员学习Linux环境内存管理

我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows程序员如何学习Linux环境内存管理。由于很多程序在Windows环境下开发好后,还要部署到Linux服务器上去,所以作为Windows程序员有必要学习Linux环境的内存…

【计算机三级网络技术】 第三篇 IP地址规划技术

IP地址规划技术 文章目录IP地址规划技术一、IP 地址规划以及划分地址新技术1.IP地址的标准分类(第一阶段)2.划分子网的三级地址结构(第二阶段)3.构成超网的无类域间路由技术(第三阶段)4.网络地址转换技术(第四阶段)二、IP 地址分类1.A类、B类与C类IP地址…

数据的表示和运算

文章目录数制与编码进制间的转换BCD码定点数与浮点数定点数是什么?浮点数是什么?定点数与浮点数的区别机器数和真值原码、反码、补码、移码基本定义整数的加减法刷题小结最后数制与编码 进制间的转换 二进制、八进制、十进制、十六进制之间的转换不用多…

前端杂学1

1.简单且必须掌握的 1.MVVM是什么 将MVC中的V变为了MVVM,实现了双向绑定。其中VM就是vue的作用,这样页面的动态化可以通过vue来操作,而不是页面直接与后端操作,实现了前后端的分离 2.为什么vue采用异步渲染 ? 调…

Kubernetes之服务发现

本文使用wordpressmysql搭建个人博客来讲解服务发现相关知识。 环境准备 wordpress需要连接到mysql才能正常工作,所以需要为mysql的pod创建一个mysql的svc,只要不删除重建svc,其IP不会变。 此时wordpress的pod需要连接mysql的svc的时候&…

HyperGBM用4记组合拳提升AutoML模型泛化能力

本文作者:杨健,九章云极 DataCanvas 主任架构师 如何有效提高模型的泛化能力,始终是机器学习领域的重要课题。经过大量的实践证明比较有效的方式包括: 利用Early Stopping防止过拟合通过正则化降低模型的复杂度使用更多的训练数…

第四阶段01-酷鲨商城项目准备

1. 关于csmall-product项目 这是“酷鲨商城”大项目中的“商品管理”项目,是一个后台管理项目(给管理员,或运营人员使用的项目,并不是普通用户使用的),并且,只会涉及与发布商品可能相关的功能开…

企业工程项目管理系统平台(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)

工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

React(二):jsx事件绑定、条件渲染、列表渲染、jsx的本质、购物车案例

React(二)一、jsx事件绑定1.this的绑定方式2.jsx中绑定this的三种方式3.事件对象和传参(1)事件对象怎么传(2)其他参数怎么传?二、条件渲染1.直接if-else2.三元表达式3.利用逻辑中断4.案例练习5.…

HTML#5表单标签

一. 表单标签介绍表单: 在网页中主要负责数据采集功能,使用<form>标签定义表单表单项: 不同类型的input元素, 下拉列表, 文本域<form> 定义表单<input> 定义表单项,通过typr属性控制输入形式<label> 为表单项定义标注<select> 定义下拉列表<o…

【GO】31.grpc 客户端负载均衡源码分析

这篇文章是记录自己查看客户端grpc负载均衡源码的过程&#xff0c;并没有太详细的讲解&#xff0c;参考价值不大&#xff0c;可以直接跳过&#xff0c;主要给自己看的。一.主要接口&#xff1a;Balancer Resolver1.Balancer定义Resolver定义具体位置为1.grpc源码对解析器(resol…