【Java】详解多线程同步的三种方式

🌺个人主页:Dawn黎明开始

🎀系列专栏:Java
每日一句:等风来,不如追风去

📢欢迎大家:关注🔍+点赞👍+评论📝+收藏⭐️


 


 

文章目录

一.🔐线程安全

1.1🔓案例引入

1.1.1🔑问题

1.1.2🔑实例操作

1.2🔓说明

二.🔐同步代码块

2.1🔓语法格式

2.2🔓全局锁

🚩实例练习1

2.3🔓任意锁

🚩实例练习2

2.4🔓局部锁

🚩实例练习3

2.5🔓this对象作为锁

🚩实例练习4

2.6🔓注意

三.🔐同步方法

3.1🔓语法格式

3.2🔓实例练习

3.3🔓思考

四.🔐同步锁(重入锁)

4.1🔓语法格式

4.2🔓实例练习


一.🔐线程安全

1.1🔓案例引入

1.1.1🔑问题

       电影院上映一部电影,共有三个窗口,请你设计一个模拟电影院卖票的程序。

1.1.2🔑实例操作

代码如下👇🏻 

package Process3;

public class SellTicket implements Runnable {
	
	private  int tickets = 20;//总票数
	private int ticketId = 1;//票号

	@Override
	public void run() {
		while (tickets>0) {
            try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++
			}
		}
	}
package Process3;

public class SellTicketDemo {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket st = new SellTicket();

		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}

运行结果👇🏻

总结:该方法不可以,有重票和漏票和多票问题。 

1.2🔓说明

           线程安全问题其实就是由多个线程同时处理共享资源所导致的。要想解决线程安全问题,必须得保证处理共享资源的代码在任意时刻只能有一个线程访问。为此,Java中提供了线程同步机制。

二.🔐同步代码块

2.1🔓语法格式

原理

      (1).当线程执行同步代码块时,首先会检查lock锁对象的标志位。

      (2).默认情况下标志位为1,此时线程会执行Synchronized同步代码块,同时将锁对象的标志位置为0。

      (3).当一个新的线程执行到这段同步代码块时,由于锁对象的标志位为0,新线程会发生阻塞,等待当前线程执行完同步代码块后。

      (4).锁对象的标志位被置为1,新线程才能进入同步代码块执行其中的代码,这样循环往复,直到共享资源被处理完为止。

 同步代码块:

 * synchronized(对象){

 * 需要同步的代码;

 * }

 *

 * (1).对象是什么呢?

 * 我们可以随便创建一个对象试试。

 * (2).需要同步的代码是哪些呢?

 * 把多条语句操作共享数据的代码的部分给包起来

 *

 * 注意

 * (1).同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。

 * (2).多个线程必须是同一把锁。

2.2🔓全局锁

注:以下四种方法的测试类都一样(只有2.2写了测试类,其余省略了)

🚩实例练习1

代码如下👇🏻 

package Sell;

public class SellTicket implements Runnable {
	
	private   int tickets = 20;//总票数
	private int ticketId = 1;//票号
	 Object o =new Object();//全局锁
	
	@Override
	public void run() {
		while (tickets>0) {
			synchronized (o) {
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++
			}
		}
				
			}
		}
	}
package Sell;


public class SellTicketDemo1 {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket1 st = new SellTicket1();

		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}

运行结果👇🏻

2.3🔓任意锁

🚩实例练习2

代码如下👇🏻 

package Sell;

public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	 Demo lock =new Demo();//任意锁
	
	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (lock) {
		    //  为什么有if判断?
	        //  如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  // 多卖21,22
			}
		}
				
			}
		}
	}
class Demo{
	//任意类
}

运行结果👇🏻

2.4🔓局部锁

🚩实例练习3

代码如下👇🏻 

package Sell;

public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号

	@Override
	public void run() {
		Object lock =new Object();//局部锁锁不住
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (lock) {
		    //  为什么有if判断?
	        //  如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  // 多卖21,22
			}
		}
				
			}
		}
	}

运行结果👇🏻

总结:该方法不可以,有重票和漏票问题。

2.5🔓this对象作为锁

🚩实例练习4

代码如下👇🏻 

package Sell;

public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号

	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (this) {
		    //  为什么有if判断?
	        //  如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  // 多卖21,22
			}
		}
				
			}
		}
	}

运行结果👇🏻

说明:项目开发中一般使用this关键字作为锁对象。

2.6🔓注意

三.🔐同步方法

3.1🔓语法格式

3.2🔓实例练习

 代码如下👇🏻 

package Sell;

public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号

	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			Selltickets ();
				
			}
		}
	//方法抽取:
	 //同步方法
	public synchronized void Selltickets () {
		if(tickets>0) {
	
			System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
			tickets--; //剩余票数--
			ticketId++; //票号++  // 多卖21,22
		}
	}
	}
package Sell;


public class SellTicketDemo1 {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket1 st = new SellTicket1();

		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}

运行结果👇🏻

3.3🔓思考

     (1).同步方法的格式及锁对象问题?

           把同步关键字加在方法上

     (2).同步代码块的锁对象是谁?

           任意全局对象   一般使用this作为锁对象

     (3).同步方法的锁是谁?

           this,它是隐含的

四.🔐同步锁(重入锁)

4.1🔓语法格式

 Lock:

 * void lock(): 获取锁。

 * void unlock():释放锁。  

 * ReentrantLock是Lock的实现类.

4.2🔓实例练习

代码如下👇🏻 

package Sell;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	Lock lock =new ReentrantLock();//同步锁(重入锁)

	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			lock.lock();//上锁
			if(tickets>0) {
				
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  
			}
			lock.unlock();//开锁
			}
		}
	}
package Sell;


public class SellTicketDemo1 {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket1 st = new SellTicket1();

		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}

运行结果👇🏻


​🌺建议大家亲自动手操作,学编程,多实践练习是提升编程技能的必经之路。

🌺欢迎大家在评论区进行讨论和指正!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用postman调用若依系统接口(报错401,认证失败,无法访问系统资源)

有时候我们想使用postman调用若依接口,会报下面的401错误,认证失败,无法访问系统资源。 原因是请求中没有token,没法通过若依的权限认证,下面来说一下如何解决。 {"msg": "请求访问:/syste…

物联网主机E6000:动环监控的新革命

多协议、多接口的全能主机 在物联网时代,数据的采集和处理已经成为了企业运营的重要环节。而物联网主机E6000,就是这个时代的全能选手。它支持多种协议和接口,无论是视频、设备还是DCS系统的数据,都能轻松接入并进行采集处理。这种…

基于SpringBoot的教务管理系统

基于SpringBoot的教务管理系统 教务管理系统开发技术功能模块代码结构运行截图数据库源码获取 教务管理系统 欢迎访问此博客,是否为自己的毕业设计而担忧呢?是否感觉自己的时间不够做毕业设计呢?那你不妨看一下下面的文章! 开发…

从房地产先后跨界通信、文旅演艺领域,万通发展未来路在何方?

近年来,房地产市场可谓负重前行,各大房企纷纷谋求新出路。 作为中国最早的房企之一,万通发展再次处在转型变革的十字路口。自去年以来,万通发展在转型升级之路上动作频频,可谓忙得不亦乐乎。 大幕落下之时,…

【word密码】word设置只读方式的四个方法

想要将word文档设置为只读模式,方法有很多,今天小奥超人介绍几个方法给大家。 方法一:文件属性 常见的、简单的设置方法,不用打开word文件,只需要右键选择文件,打开文件属性,勾选上【只读】选…

Java多线程之CAS及原子操作

一、CAS是什么? Java 并发机制实现原子操作有两种: 一种是锁,还有一种是CAS。 在Java中,锁在并发处理中占据了一席之地,但是使用锁有一个不好的地方,就是当一个线程没有获取到锁时会被阻塞挂起&…

35 _ Trie树:如何实现搜索引擎的搜索关键词提示功能?

搜索引擎的搜索关键词提示功能,我想你应该不陌生吧?为了方便快速输入,当你在搜索引擎的搜索框中,输入要搜索的文字的某一部分的时候,搜索引擎就会自动弹出下拉框,里面是各种关键词提示。你可以直接从下拉框中选择你要搜索的东西,而不用把所有内容都输入进去,一定程度上…

OpenCV入门2——图像视频的加载与展示一些API

文章目录 题目OpenCV创建显示窗口OpenCV加载显示图片题目 OpenCV保存文件利用OpenCV从摄像头采集视频从多媒体文件中读取视频帧将视频数据录制成多媒体文件OpenCV控制鼠标关于[np.uint8](https://stackoverflow.com/questions/68387192/what-is-np-uint8) OpenCV中的TrackBar控…

Rust图形界面编程:egui平直布局

文章目录 平直布局with_layout 平直布局 在前面的示例中,已经用到了ui.horizontal用来布局,其特点是水平摆放控件。相应地,ui.vertical则是垂直摆放控件。根据控件的摆放顺序不同,这两个布局组件衍生出一系列布局函数 horizonta…

21 Linux 自带的LED驱动

一、Linux 自带 LED 驱动使能 其实 Linux 内核自带 LED 抢夺那个,但在此之前需要配置 Linux 驱动来使能 LED 驱动。 输入以下命令: cd linux/atk-mpl/linux/my_linux/linux-5.4.31 make menuconfig 根据以下路径找到 LED 驱动: → Device D…

MySQL表的增查(进阶)

目录 1.插入查询结果 2.查询 2.1聚合查询 2.1.1聚合函数 2.1.2GROUP BY子句 2.1.3HAVING 2.2联合查询 2.2.1内连接 2.2.2外连接 2.2.3自连接 2.3子查询 2.4合并查询 1.插入查询结果 在一张表中插入另一张表的查询结果。 语法为: insert into 表名 (列…

【算法】区间(差分约束)

题目 给定 n 个区间 [ai,bi] 和 n 个整数 ci。 你需要构造一个整数集合 Z,使得 ∀i∈[1,n],Z 中满足 ai≤x≤bi 的整数 x 不少于 ci 个。 求这样的整数集合 Z 最少包含多少个数。 输入格式 第一行包含整数 n。 接下来 n 行,每行包含三个…

解决Github上的README无法显示图片

首先感谢博主的思路:思路 最近写了点东西提交到git 发现本地能查看md里的图片用的相对路径,提交到github就看不见,并且发现不只是我自己的仓库看不见,其他人的我也看不见。那就有问题了 解决:正常使用相对路径&…

【BIM入门实战】Revit图元的选择方式,总有一款适合你

Revit图元的五种常见选择方式,总有一款适合你。 文章目录 一、直接单击二、加选和减选三、连续框选四、按类别选择五、全选过滤选择操作可以在三维视图、平面视图等多种视图中进行。 一、直接单击 直接单击,即可选中某一个图元,如选择一个扶手。 二、加选和减选 按住ctrl键…

32 _ 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?

从今天开始,我们来学习字符串匹配算法。字符串匹配这样一个功能,我想对于任何一个开发工程师来说,应该都不会陌生。我们用的最多的就是编程语言提供的字符串查找函数,比如Java中的indexOf(),Python中的find()函数等,它们底层就是依赖接下来要讲的字符串匹配算法。 字符串…

C++二分查找算法:数组中占绝大多数的元素

题目 设计一个数据结构,有效地找到给定子数组的 多数元素 。 子数组的 多数元素 是在子数组中出现 threshold 次数或次数以上的元素。 实现 MajorityChecker 类: MajorityChecker(int[] arr) 会用给定的数组 arr 对 MajorityChecker 初始化。 int query(int left, …

37 _ 贪心算法:如何用贪心算法实现Huffman压缩编码?

基础的数据结构和算法我们基本上学完了,接下来几节,我会讲几种更加基本的算法。它们分别是贪心算法、分治算法、回溯算法、动态规划。更加确切地说,它们应该是算法思想,并不是具体的算法,常用来指导我们设计具体的算法和编码等。 贪心、分治、回溯、动态规划这4个算法思想…

622.设计循环队列(LeetCode)

思路 先确定什么情况为空,什么情况为满。 这里有两种解决方案, 1.留一个空间空置,当rear1 front时 ,则队列为满 (这里我们选用方案一) 2.增加一个size变量记录数据个数,size 0则为空&#xff…

Uniapp导出的iOS应用上架详解

​ 目录 Uniapp导出的iOS应用上架详解 摘要 引言 苹果审核标准 苹果调试 注意事项和建议 总结 摘要 本文将探讨Uniapp导出的iOS应用能否成功上架的问题。我们将从苹果审核标准、性能影响、调试流程等多个方面进行深入分析,以及向开发者提供相关注意事项和建…

【计算机网络笔记】DHCP协议

系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…