Java多线程编程(三)——线程锁

article/2025/10/20 18:56:58

卖票案例

同步代码块解决数据安全问题

同步方法解决数据安全问题

同步方法的格式:

同步方法和同步方法块的区别:

同步静态方法

Lock锁 


卖票案例

某电影院目前正在上映国产大片,共有30张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

  • 实现步骤

    • 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int tickets = 100;

    • 在Ticket类中重写run()方法实现卖票,代码步骤如下

    • 判断票数大于0,就卖票,并告知是哪个窗口卖的

    • 卖了票之后,总票数要减1

    • 票卖没了,线程停止

    • 定义一个测试类Demo,里面有main方法,代码步骤如下

    • 创建Ticket类的对象

    • 创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称

    • 启动线程

如果不使用同步锁 Coding实现如下:

public class Ticket implements Runnable {private int tickets = 100;//在Ticket类中重写run()方法实现卖票,代码步骤如下@Overridepublic void run() {while (true) {if(ticket <= 0){//卖完了break;}else{try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}ticket--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");}}}
}
public class Demo {public static void main(String[] args) {//创建Ticket类的对象Ticket st = new Ticket();//创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称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个bug:

1.相同的票出现了多次、2.出现了负数的票

问题产生原因

线程执行的随机性导致的,Thread实例化对象t1,t2,t3对CPU的使用权的抢占是不确定的

可能在卖票过程中丢失cpu的执行权,(代码执行的每一行,如果不控制,CPU执行权都有可能被抢走)导致出现问题。

代码中sleep(100),指代实际买票时出票导致的延迟,假设t1,t2,t3都在sleep,t1先抢到CPU执行权,执行ticket--后,ticket值是99

此时,执行权又被t2抢走,ticket再执行ticket--操作,ticket的值为98

 又因为我们的程序是三个线程同时操作一个数据(ticket),所以打印出来的结果ticket的值都是98

 出现负号票的原因也类似。

同步代码块解决数据安全问题

安全问题出现的条件

是多线程环境

有共享数据

有多条语句操作共享数据

如何解决多线程安全问题呢?

基本思想:让程序没有发生安全问题的环境

怎么实现呢?

把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可

Java提供了同步代码块的方式来解决

同步代码块格式:

synchronized(任意对象) { 多条语句操作共享数据的代码 
}

同步的好处和弊端

好处:解决了多线程的数据安全问题

弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

Coding: 

public class demo {public static void main(String[] args) {Ticket ticket = new Ticket();Thread t1 = new Thread(ticket);Thread t2 = new Thread(ticket);Thread t3 = new Thread(ticket);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}
public class Ticket implements Runnable{private int ticket = 100;private Object obj = new Object();@Overridepublic void run() {while(true) {//使用同步解决//注意synchronized()中的锁对象必须是唯一的synchronized (obj) {if(ticket == 0) {break;}else {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}ticket--;System.out.println(Thread.currentThread().getName()+ "在卖票,还剩下" + ticket);}}}}
}

同步方法解决数据安全问题

同步方法的格式:

就是把synchronized关键字加到方法上

修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体;
}

同步方法和同步方法块的区别:

(1)同步代码块可以锁住指定代码,而同步代码块锁住方法里面的所有代码。

(2)同步代码块可以指定锁对象,同步方法不能指定锁对象,其锁对象是this。

public class MyRunnable implements Runnable {private int ticketCount = 100;@Overridepublic void run() {while(true){if("窗口一".equals(Thread.currentThread().getName())){//同步方法boolean result = synchronizedMthod();if(result){break;}}if("窗口二".equals(Thread.currentThread().getName())){//同步代码块synchronized (this){if(ticketCount == 0){break;}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticketCount--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");}}}}}private synchronized boolean synchronizedMthod() {if(ticketCount == 0){return true;}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticketCount--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");return false;}}
}
 public class Demo {public static void main(String[] args) {MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName("窗口一");t2.setName("窗口二");t1.start();t2.start();}
}

上述案例,“窗口一”使用同步方法,“窗口二”使用同步代码块,效果是一致的。

同步静态方法

格式:

修饰符 static synchronized 返回值类型 方法名(方法参数) { 方法体;
}

 同步静态方法的锁对象?

类名.class

public class MyRunnable implements Runnable {private static int ticketCount = 100;@Overridepublic void run() {while(true){if("窗口一".equals(Thread.currentThread().getName())){//同步方法boolean result = synchronizedMthod();if(result){break;}}if("窗口二".equals(Thread.currentThread().getName())){//同步代码块synchronized (MyRunnable.class){if(ticketCount == 0){break;}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticketCount--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");}}}}}private static synchronized boolean synchronizedMthod() {if(ticketCount == 0){return true;}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticketCount--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");return false;}}
}

Lock锁 

虽然我们可以理解同步代码块和同步方法锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

Lock提供了获得锁与释放锁的操作

void lock()   //获得锁  
void unlock() //释放锁 

利用上面那个案例来演示如下 

 Coding:

import java.util.concurrent.locks.ReentrantLock;public class Ticket implements Runnable {//票的数量private int ticket = 100;private Object obj = new Object();private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true) {//格式化的时候可以选中代码 ctrl + Alt + ttry {lock.lock();if (ticket <= 0) {//卖完了break;} else {Thread.sleep(100);ticket--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}}
}
public class Demo {public static void main(String[] args) {Ticket ticket = new Ticket();Thread t1 = new Thread(ticket);Thread t2 = new Thread(ticket);Thread t3 = new Thread(ticket);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}
}

这里使用了try-catch-finally,可以使用 Ctrl + Alt + T ,来设置。

阅读下章可见

Java多线程编程(四)——死锁问题icon-default.png?t=LBL2https://blog.csdn.net/weixin_43715214/article/details/122375269?spm=1001.2014.3001.5501


http://chatgpt.dhexx.cn/article/GRikYXxZ.shtml

相关文章

浅析Java 多线程中的锁

前言 随着互联网技术的快速发展&#xff0c;多线程编程已经成为了现今编程领域中必不可少的知识点之一。Java 是一种广泛使用的编程语言&#xff0c;也是一些底层应用程序和高并发应用程序的首选语言。而 Java 提供的多线程编程机制和相关的锁机制&#xff0c;则成为了 Java 开…

【Java多线程进阶】常见的锁策略

前言 众所周知&#xff0c;拳击运动员是要分等级&#xff08;轻量级、重量级等等&#xff09;来参加比赛的&#xff0c;在 Java 多线程中 锁&#xff08;synchronized&#xff09; 也会根据锁的竞争程度来升级为相关“高等级”锁&#xff0c;为了更好的理解 synchronized 加锁机…

Java多线程下——各类锁的详解

这里写目录标题 各类锁的详解常见的锁策略乐观锁 vs 悲观锁读写锁重量级锁 vs 轻量级锁自旋锁&#xff08;Spin Lock&#xff09;公平锁 vs 非公平锁可重入锁 vs 不可重入锁 CASSynchronized 原理偏向锁锁消除锁粗化 Callable 接口ReentrantLock线程池ExecutorService 和 Execu…

Java中的线程和锁机制

线程池 为什么使用线程池&#xff1f;线程池执行原理&#xff1f;线程池参数有哪些&#xff1f;线程池大小怎么设置&#xff1f;线程池的类型有哪些&#xff1f;适用场景&#xff1f; 进程线程 线程的生命周期讲一下线程中断&#xff1f;创建线程有哪几种方式&#xff1f;什么是…

【Java】中的多线程线程锁

多线程 文章目录 多线程线程的创建和启动sleep()stop() 线程的休眠和中断线程的优先级线程的礼让和加入yield()stop() 线程锁和线程同步synchronized 关键字 死锁概念 wait & notify methodThreadLocal的使用定时器 Timer守护线程再谈集合类parallelStreamforEachOrdered()…

Java多线程中 的各种锁

学习 java 多线程时&#xff0c;最头疼的知识点之一就是 java 中的锁了&#xff0c;什么互斥锁、排它锁、自旋锁、死锁、活锁等等&#xff0c;细分的话可以罗列出 20 种左右的锁&#xff0c;光是看着这些名字就足以让人望而却步了&#xff0c;更别说一个个去理解它们的含义了。…

Java——多线程和锁

多线程 前言:当我们打开一个网站时&#xff0c;不同部分的加载并不是先后出现的&#xff0c;是并行出现的&#xff0c;没有出现一个地方没加载完&#xff0c;别的地方就也加载不出来这种事。这个就是多线程并行运行。 当其中一个线程发生阻塞时&#xff0c;操作系统会自动执行…

Java-多线程中的“锁“

文章目录 Java多线程中的锁1. 什么是锁&#xff1f;2. 锁的作用3. 锁的类型4. 锁的使用示例5.乐观锁和悲观锁6. 锁的注意事项总结 Java多线程中的锁 在Java多线程编程中&#xff0c;锁是一种重要的同步机制&#xff0c;用于保护共享资源的访问。使用锁可以防止多个线程同时对共…

JAVA三种线程锁

内置锁&#xff1a;synchriozed&#xff0c;关键字&#xff0c;同步代码块&#xff0c;object.wait和object.notify/notifyall 显示锁&#xff1a;Lock&#xff0c;JUC包下的类&#xff0c;同步代码块&#xff0c;condition.await和condition.signal/signalall 原子类&#xff…

Java多线程中锁的理解与使用

1.简介 锁作为并发共享数据&#xff0c;保证一致性的工具&#xff0c;在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等 ) 。 2.Java锁的种类 公平锁/非公平锁可重入锁独享锁/共享锁互斥锁/读写锁乐观锁/悲观锁分段锁偏向锁/轻量级锁/重量级锁自旋锁 上面是很多锁…

java多线程的15种锁

1 java锁分类 下面我们依照序号依次的介绍每一种锁 2 悲观锁和乐观锁 悲观锁和乐观锁是一种广义的概念&#xff0c;体现的是看待线程同步的不同的角度 悲观锁认为自己在使用数据的时候&#xff0c;一定有别的线程来修改数据&#xff0c;在获取数据的时候会先加锁&#xff0c…

Java多线程 - 锁

Java多线程 - 锁 三性 可见性 指的是线程之间的可见性&#xff0c;一个线程对状态的修改&#xff0c;对其他线程是可见的。在 Java中 volatile、synchronized 和 final 实现可见性。 原子性 如果一个操作是不可分割的&#xff0c;我们则称之为原子操作&#xff0c;也就是有原…

Java多线程与锁

前文中&#xff0c;我们已经了解了什么是线程&#xff0c;线程间常用通信方式&#xff0c;线程池以及其相关特性&#xff0c;可以看出锁在多线程环境中充当着重要作用&#xff0c;不管是线程间的数据通信&#xff0c;还是线程间的等待和唤醒&#xff0c;都依赖于锁&#xff0c;…

JAVA基础-多线程中锁机制

多线程锁 多线程锁机制锁的定义锁的分类公平锁/非公平锁可重入锁独享锁/共享锁互斥锁/读写锁乐观锁/悲观锁分段锁偏向锁/轻量级锁/重量级锁自旋锁 锁的使用AQSAQS框架展示AQS定义两种资源共享方式AQS常用的几种方法&#xff08;自定义同步器实现时&#xff09;自定义同步器实现…

多线程系列-Java中的锁(简介)

前言 Java提供了种类丰富的锁&#xff0c;每种锁因其特性的不同&#xff0c;在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码&#xff08;本文中的源码来自JDK 8和Netty 3.10.6&#xff09;、使用场景进行举例&#xff0c;为读者介绍主流锁的知识点&#xff0c;以…

JAVA如何在线程中加锁(四种方法)

JAVA多线程锁 线程的生命周期 ​ 总共六种状态&#xff0c;可归结为五种&#xff0c;线程的最终是死亡&#xff0c;阻塞不是最终状态&#xff0c;只是一个临时状态。只有调用了start方法&#xff0c;线程才进入就绪阶段。 //新生 ​ NEW, //运行​ RUNNABLE, //阻塞​ BLOCKE…

大数据平台_大数据应用场景有哪些

大数据时代的出现&#xff0c;简单的讲是海量数据同完美计算能力结合的结果&#xff0c;确切的说是移动互联网、物联网产生了海量的数据&#xff0c;大数据计算技术完美地解决了海量数据的收集、存储、计算、分析的问题。一些公司也成立了大数据部门&#xff0c;大数据得到了企…

银行业9大数据科学应用案例

在银行业中使用数据科学不仅仅是一种趋势,它已成为保持竞争的必要条件。 银行必须认识到, 大数据技术可以帮助他们有效地集中资源,做出更明智的决策并提高绩效。 以下我们罗列银行业使用的数据科学用例清单, 让您了解如何处理大量数据以及如何有效使用数据。 [TOC] 1 欺诈识…

某银行大数据体系架构设计与演进

近年来&#xff0c;随着大数据与人工智能相关技术的迅速发展&#xff0c;新技术逐步在全社会各行各业得到应用。银行业作为一个高度信息化的行业&#xff0c;首当其冲面临着互联网新技术应用的挑战。民生银行在 2013 年开始布局分布式、大数据及人工智能技术等领域&#xff0c;…

图解大数据 | 大数据生态与应用导论

作者&#xff1a;韩信子[ShowMeAI](https://www.showmeai.tech 教程地址&#xff1a;https://www.showmeai.tech/tutorials/84 本文地址&#xff1a;https://www.showmeai.tech/article-detail/167 声明&#xff1a;版权所有&#xff0c;转载请联系平台与作者并注明出处 收藏S…