java多线程基本操作方法

目录

一、isAlive()

二、 join()

三、start和run

四、 volatile()

五、synchronized

1、synchronized 引入

2、死锁 

第一种情况:反复加锁

第二种情况 对不同对象嵌套加锁的死锁

3、形成死锁条件 

六、wait和notify


一、isAlive()

isAlive表示线程时候结束,判断时候回调函数(这里是run)时候结束.

public class Test2_isAlive {public static void main(String[] args) {Thread t=new Thread(()->{System.out.println("线程开始");try {Thread.sleep(1000);//只是用来让程序暂时休息下,方便看结果} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程结束");});t.start();//其次被执行System.out.println(t.isAlive());//首先被执行,因为t.start需要准备一些资源try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t.isAlive());//最后执行,会是false,也可能run中执行的慢也会被提前执行,true}
}

第一种情况,run还没有执行完毕,也就是回调函数还没有执行完毕,执行了最后一次System.out.println(t.isAlive());

 

第二种情况,run执行完毕了,在执行最后一次 System.out.println(t.isAlive());

   我们可以加大sleep()的时间来选择这两次结果

二、 join()

这是一个等待函数,创建两个线程A 和 B,对应的实例分别是a和b,在A中用b调用了join(),那么A线程会被暂停,等待B线程执行完毕在执行

public class Thread_join {public static void main(String[] args) throws InterruptedException {Thread t=new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("t线程工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();System.out.println("join开始等待");t.join();System.out.println("join等待结束");}
}

描述: 我们在main线程中使用Thread的t实例调用了join()方法,main需要等待Thread 执行完毕在执行

三、start和run

四、 volatile()

我们先来聊聊什么叫内存可见性?

我们先来看一下下面代码,这里定义了一个变量flag通过线程t1判断flag的值看是否循环,通过改变t2来改变flag的值终止t1的循环,我们预期输入1来终止t1的循环

public class Thread_volatile {public static int flag=0;public static void main(String[] args) {Thread t1=new Thread(()->{while (flag==0){//这个循环一秒钟会执行很多次}System.out.println("t线程推出");});t1.start();Thread t2=new Thread(()->{System.out.println("请输入flag值");Scanner scan=new Scanner(System.in);flag=scan.nextInt();});t2.start();}
}

程序进入了死循环,为啥呢? 

这就是内存的可见性问题,编译器在编译t1的进程while循环时,发现每次需要从内存中读取flag的值在进行while条件判断,由于这个while循环执行非常快,短时间内需要大量读flag的值,在判断,但是这个从内存读取flag的值的速度时判读的速度的千分之一甚至是万分之一倍,所以编译器决定只进行一次读取,进行大量判断,编译器初心是为了提高代码性能,如果放在单线程中没有问题,但是这是多线程。

解决方案(不让编译器自动优化代码了,就是进行多次读取)

给flag加上volatile修饰

 public volatile static int flag=0;

五、synchronized

1、synchronized 引入

synchronized 会起到互斥效果 , 某个线程执行到某个对象的 synchronized 中时 , 其他线程如果也执行到 同一个对象 synchronized 就会 阻塞等待
  • 进入 synchronized 修饰的代码块, 相当于 加锁
  • 退出 synchronized 修饰的代码块, 相当于 解锁

我们先来看一组简单代码:

public class Test_Synchronized {public static int count=0;public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for (int i = 0; i < 5000; i++) {count++;}});Thread t2=new Thread(()->{for (int i = 0; i <5000 ; i++) {count++;}});t1.start();t2.start();t1.join();//这里不加join会在没有加完情况下打印count的值t2.join();System.out.println(count);}
}

描述:创建两个线程每个线程让count变量加5k次,最后我们的预期结果应该是1w。可是如下:

 

原因分析: count++这个操作本质上,是分为三部进行的

  1. load  把数据从内存读取到cpu寄存器中
  2. add   把寄存器中的数据加1
  3. save  把寄存器中的数据保存到内存

我们的理想状态是两个线程分别执行count++,可是线程之间是并发执行的呀,假设count开始值是0,如果我们在执行a线程的count++的时候就是上面count++三部曲,执行到了第二步,如果这个时候b线程也开始执行三部曲的第一步开时读取count的值那肯定是和a线程读取的一样都是0,因为这时候a线程还没有改变count的值。继续往后走,当a线程执行完毕三部曲,count变成1,a线程继续读取继续执行,这时候b线程执行完了,把count也变成了1,b线程继续执行。发现问题了吗?

解决方案一

我们先执行完毕一个线程,在执行一个另外一个线程,代码如下:

        t1.start();t1.join();//这里不加join会在没有加完情况下打印count的值t2.start();t2.join();System.out.println(count);

解决方案二

使用synchronized加锁,如果加锁的对象一样那么后者需要等待前者加锁部分执行完毕在执行

举个例子理解:多个人等待上一个厕所,这个厕所同一时间只能允许一个上,上厕所都要锁门,假设现在a在上厕所,后面依次是b,c,d在排队,当a上完厕所出来,后面下一个上厕所的人不一定是b这是有优先级的,而且也不是下一个人立刻上厕所,有一个cpu调用过程

public class Test_Synchronized {public static int count=0;public static void main(String[] args) throws InterruptedException {Object object=new Object();Thread t1=new Thread(()->{synchronized (object){for (int i = 0; i < 5000; i++) {count++;}}});Thread t2=new Thread(()->{synchronized (object){for (int i = 0; i < 5000; i++) {count++;}}});t1.start();t2.start();t1.join();//这里不加join会在没有加完情况下打印count的值t2.join();System.out.println(count);}
}

2、死锁 

第一种情况:反复加锁

举个例子:我们已经对一个对象加锁a了,但是现在我们在这个锁里面在对这个对象加锁b,这样外层锁执行到b锁的时候,b锁要等待这个已经加a锁的对象执行完毕在执行,可是a锁又要执行b锁才能执行完,程序就卡住了

解决方案 

第二种情况 对不同对象嵌套加锁的死锁

举个例子:你想开车,但是车钥匙在房间里,你想进房间拿钥匙,但是房间钥匙在锁在了你想开的车里,很离谱把,就是这么离谱,哈哈哈

代码什么情况下会出现死锁?

public class Deal_Synchronized2 {public static void main(String[] args) {Object object1=new Object();Object object2=new Object();Thread t1=new Thread(()->{synchronized (object1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object2){System.out.println("t1加锁完毕");}}});Thread t2=new Thread(()->{synchronized (object2){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object1){System.out.println("t2加锁完毕");}}});t1.start();t2.start();}
}

描述:t1线程和t2线程开始都会先获得object1的锁和object2的锁,当t1线程执行到synchronized (object2)的时候,t2对象已经对object2加上锁了,t1进程进入阻塞态等待object2释放,但是t2线程的object2需要等待t1线程执行完毕,所以程序进入阻塞态

解决方案

在编写锁的时候,注意先编写序号小的锁,后编写序号大的锁(或先大后小)要有一定规则,修改代码如下:

public class Deal_Synchronized2 {public static void main(String[] args) {Object object1=new Object();Object object2=new Object();Thread t1=new Thread(()->{synchronized (object1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object2){System.out.println("t1加锁完毕");}}});Thread t2=new Thread(()->{synchronized (object1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object2){System.out.println("t2加锁完毕");}}});t1.start();t2.start();}
}

3、形成死锁条件 

  1. 互斥使用.(锁的基本特性)当一个线程持有一把锁的时候,另一个线程也想获取到锁,就要阻塞等待
  2. 不可抢占(锁的基本特性)当锁已经被线程1拿到之后,线程2只能等待线程1主动释放
  3. 请求保持(代码决定)一个线程尝试获取多把锁(有锁1了还想得到锁2,在获取锁2的过程不会释放锁1)
  4. 循环嵌套锁,你想开车,但是车钥匙在房间里,你想进房间拿钥匙,但是房间钥匙在锁在了你想开的车里

我们在以后解决的主要针对第4条就可以,即注意锁的编号

六、wait和notify

wait是在线程还没有结束的时候进入阻塞,join是影响线程的结束顺序,wait需要用notify唤醒

public class Wait_Notify {public static void main(String[] args) {Object object=new Object();Thread t1=new Thread(()->{synchronized (object){System.out.println("wait之前");try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait之后");}});Thread t2=new Thread(()->{try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object){System.out.println("进行通知");object.notify();}});t1.start();t2.start();}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷粒商城——Redisson看门狗

可重入锁&#xff1a; 看门狗机制&#xff1a;(lock.lock()不设置过期时间就会自动触发 看门狗机制) 如果一个线程已经上锁后&#xff0c;在运行的过程中中断导致未释放锁从而导致其他线程无法进行&#xff0c;为此需要为每个锁设置自动过期时间。但是如果线程运行时间较长&am…

LVGL线条和画布功能

线条部件 线条部件由多个点连接而成&#xff0c;它可用于修饰界面或者展示数据。 要注意这里的描述&#xff0c;线条是由多个点连接而成的。 线条部件只有一个组成部分&#xff1a;主体 LV_PART_MAIN 线条是由多个点连接而成的对象&#xff0c;用户可以使用 lv_point_t 类型的…

“Hands-free AG audio“和“Stereo“的区别

用蓝牙连接耳机后&#xff0c;发现有两个选项 一个音量大&#xff0c;一个音质好&#xff0c;好奇去查了查。 “Hands-free AG audio”&#xff08;自由通话音频&#xff09;是指一种技术或功能&#xff0c;可以使您在进行通话时无需使用手部操作或接触设备。这通常适用于汽车…

vue3从精通到入门2:虚拟DOM的生成与真实DOM的转化

虚拟 DOM 实现是 Vue 框架的核心部分之一&#xff0c;它负责在真实 DOM 之上抽象出一个轻量级的、可复用的 JavaScript 对象树&#xff0c;用于高效地更新视图。 什么是虚拟DOM? 虚拟 DOM 是一个编程概念&#xff0c;它将真实的 DOM 树抽象为一个轻量级的 JavaScript 对象树…

Lua模拟面向对象

13.2 逻辑热更新——Lua2-2_哔哩哔哩_bilibili parent中添加 模拟面向对象

Django之Web应用架构模式

一、Web应用架构模式 在开发Web应用中,有两种模式 1.1、前后端不分离 在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示。前端与后端的耦合度很高 1.2、前后端分离 在前后端分离的应用模式中,后端仅返…

蓝桥杯学习笔记 单词分析

试题 G: 单词分析 时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分 [问题描述] 小蓝正在学习一门神奇的语言&#xff0c;这门语言中的单词都是由小写英文字母组成&#xff0c;有些单词很长&#xff0c;远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词&#xf…

Rust使用原始字符串字面量实现Regex双引号嵌套双引号正则匹配

rust使用Regex实现正则匹配的时候&#xff0c;如果想实现匹配双引号&#xff0c;就需要使用原始字符串字面量&#xff0c;不然无法使用双引号嵌套的。r#"..."# 就表示原始字符串字面量。 比如使用双引号匹配&#xff1a; use regex::Regex;fn main() {println!(&qu…

Spring MVC学习记录

一、MVC模式 1. MVC模型&#xff1a;一种软件架构模式 Model-View-Controller&#xff08;模型-视图-控制器&#xff09;模式&#xff0c;目标是将软件的用户界面&#xff08;即前台页面&#xff09;和业务逻辑分离&#xff0c;使代码具有更高的可扩展性、可复用性、可维护性以…

Shadow Tactics

本题链接&#xff1a; 题目&#xff1a; 样例&#xff1a; 输入 1 1 3 3 U 2 2 2 输出 YES 思路&#xff1a; 根据题意&#xff0c;隼人的坐标是不会动的&#xff0c;并且士兵只能直线来回行动。 所以这里我们需要分成三种情况。 1、隼人坐标在士兵走动路线之间&#xff0c;…

东方博宜 1963. 贝贝的选择

东方博宜 1963. 贝贝的选择 #include<iostream> using namespace std; int main() {int n ;cin >> n ;int cnt ;cnt 0 ;for(int i 1 ; i < n ; i){int j ;j i ;int g ;while(j>0){g j % 10 ;if (g6){cnt 1 ;break ;}j j / 10 ; }}if (cnt % 2 0)cou…

Scala介绍与环境搭建

Scala环境搭建与介绍 一、Scala环境搭建 1、环境准备与下载 2、验证Scala 3、IDEA新建项目&#xff0c;配置Scala&#xff0c;运行Hello world 二、Scala介绍 1、Scala 简介 2、Scala 概述 一、Scala环境搭建 1、环境准备与下载 JDK1.8 Java Downloads | Oracle 下载需求版本…