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

news2024/11/26 14:54:09

🌺个人主页: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();
	}
}

运行结果👇🏻


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

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

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

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

相关文章

MAC地址_MAC地址格式_以太网的MAC帧_详解

MAC地址 全世界的每块网卡在出厂前都有一个唯一的代码,称为介质访问控制(MAC)地址 一.网络适配器(网卡) 要将计算机连接到以太网,需要使用相应的网络适配器(Adapter),网络适配器一般简称为“网卡”。在计算机内部,网卡与CPU之间的通信&…

【UE C++】读取文本文件,并解析

目录 0 引言1 空格 制表符 换行符1.1 定义1.2 查看字符 2 实战 🙋‍♂️ 作者:海码007📜 专栏:UE虚幻引擎专栏💥 标题:❣️ 寄语:书到用时方恨少,事非经过不知难!&#x…

C进阶---字符函数和字符串函数

目录 一、长度不受限限制的字符串函数 1.1strlen 1.2strcpy 1.3strcat 1.4strcmp 二、长度受限制的字符串函数 2.1strncpy 2.2strncat 2.3strncmp 三、其他字符串函数 3.1strstr 3.2strtok 3.3sterror 3.4memcpy 3.5memmove 3.6memcmp 四、字符分类函…

c语言:如何打印杨辉三角形。

题目:打印杨辉三角形 如: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 思路和代码: //由规律不难得出,每行首个数字和最后最后一个数字都为1,其余数字是这列的上一个数字和前一个数字的和组成,由此推出代码 #de…

Openssl X509 v3 AuthorityKeyIdentifier实验与逻辑分析

Openssl是X509的事实标准,目前主流OS或个别安全性要求较高的设计场景,对X509的证书链验证已经不在停留在只从数字签名校验了,也就是仅仅从公钥验签的角度,在这些场景中,往往还会校验AuthorityKeyIdentifier和SubjectKe…

【文件包含】metinfo 5.0.4 文件包含漏洞复现

1.1漏洞描述 漏洞编号————漏洞类型文件包含漏洞等级⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐漏洞环境windows攻击方式 MetInfo 是一套使用PHP 和MySQL 开发的内容管理系统。MetInfo 5.0.4 版本中的 /metinfo_5.0.4/about/index.php?fmodule文件存在任意文件包含漏洞。攻击者可利用漏洞读取网…

第十九章 Java绘图

第十九章 java绘图 19.1 java绘图类 绘图时高级程序设计中非常重要的技术,例如,应用程序可以绘制闪屏图片,背景图片,组件外观等等,Web程序可以绘制统计图,数据库存储图片资源等,真骨耸为“一…

服务名无效。 请键入 NET HELPMSG 2185以获得更多的帮助

遇到的问题是MySQL服务没有。 因为net start 服务名,启动的是win下注册的服务。此时,我系统中并没有注册mysql到服务中。即下面没有mysql服务。 mysqld --install net start mysql

2023.11.15 每日一题(AI自生成应用)【C++】【Python】【Java】【Go】 动态路径分析

目录 一、题目 二、解决方法 三、改进 一、题目 背景: 在一个城市中,有数个交通节点,每个节点间有双向道路相连。每条道路具有一个初始权重,代表通行该路段的成本(例如时间、费用等)。随着时间的变化&am…

nodejs+vue黄河风景线旅游网站的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

本文首先对该系统进行了详细地描述,然后对该系统进行了详细的描述。管理人员增加了系统首页、个人中心、用户管理、景点分类管理、景点简介管理、旅游路线管理、文章分类管理、公告文章管理、系统管理理等功能。这套黄河风景线旅游网站是根据当前的现实需要&#xf…

SystemVerilog学习 (5)——接口

一、概述 验证一个设计需要经过几个步骤: 生成输入激励捕获输出响应决定对错和衡量进度 但是,我们首先需要一个合适的测试平台,并将它连接到设计上。 测试平台包裹着设计,发送激励并且捕获设计的输出。测试平台组成了设计周围的“真实世界”,…

【miniQMT实盘量化3】获取历史行情数据

前言 上篇文章,介绍了如何与miniQMT建立连接,这篇开始,我们会深入探讨miniQMT的每个功能接口。首先,从获取历史数据开始。 迅投的官方文档目前已经更新,miniQMT对应原生API部分 接口汇总 与历史行情数据相关的接口&a…

Solidity案例详解(四)投票智能合约

该合约为原创合约,功能要求如下 在⼀定时间能进⾏投票超过时间投票截⽌,并投赞同票超过50%则为通过。 使⽤safeMath库,使⽤Owner 第三⽅库拥有参与投票权的⽤户在创建合约时确定Voter 结构 要有时间戳、投票是否同意等;struct 结构…

浅尝:iOS的CoreGraphics和Flutter的Canvas

iOS的CoreGraphic 基本就是创建一个自定义的UIView&#xff0c;然后重写drawRect方法&#xff0c;在此方法里使用UIGraphicsGetCurrentContext()来绘制目标图形和样式 #import <UIKit/UIKit.h>interface MyGraphicView : UIView endimplementation MyGraphicView// Onl…

智能AI系统ChatGPT网站源码+支持OpenAI DALL-E3文生图+支持ai绘画(Midjourney)/支持GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

【命令行魔法:掌握Linux基础工具开发的独门技艺】

本节目标 1.Linux 软件包管理器 2.Linux开发工具 3.Linux编译器-gcc/g使用 4.Linux项目自动化构建工具-make/Makefile 5.Linux第一个小程序&#xff0d;进度条 1.Linux 软件包管理器 yum 什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译…

k8s资源管理操作——陈述式管理方式

目录 陈述式资源管理方式 1、常用的kubernetes管理命令 1&#xff09;查看版本信息 2&#xff09;查看资源对象简写 3&#xff09;查看集群信息 4&#xff09;配置kubectl自动补全 5&#xff09;node节点查看日志 2、资源管理命令 1&#xff09;创建资源 2&#xff0…

保姆级教程之SABO-VMD-CNN-SVM的分类诊断,特征可视化

今天出一期基于SABO-VMD-CNN-SVM的分类诊断。 依旧是采用经典的西储大学轴承数据。基本流程如下&#xff1a; 首先是以最小包络熵为适应度函数&#xff0c;采用SABO优化VMD的两个参数。其次对每种状态的数据进行特征向量的求取&#xff0c;并为每组数据打上标签。然后将数据送入…

Qt控件按钮大全

​ 按钮 在 Qt 里,最常用使用的控件就是按钮了,有了按钮,我们就可以点击,从而响应事件,达到人机交互的效果。不管是嵌入式或者 PC 端,界面交互,少不了按钮。Qt 按钮部件是一种常用的部件之一,Qt 内置了六种按钮部件如下: (1) QPushButton:下压按钮 (2) QToolBu…

使用docker部署ELK日志框架-Elasticsearch

一、ELK知识了解 1-ELK组件 工作原理&#xff1a; &#xff08;1&#xff09;在所有需要收集日志的服务器上部署Logstash&#xff1b;或者先将日志进行集中化管理在日志服务器上&#xff0c;在日志服务器上部署 Logstash。 &#xff08;2&#xff09;Logstash 收集日志&#…