Java 线程安全与锁

article/2025/10/20 18:49:26

多线程内存模型

  • 线程私有栈内存
    • 每个线程 私有的内存区域
  • 进程公有堆内存
    • 同一个进程 共有的内存区域

为什么会有线程安全问题?

  • 多个线程同时具有对同一资源的操作权限,又发生了同时对该资源进行读取、写入的情况,那么就会出现重复操作的情况

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

什么是锁?

锁就是对于操作资源的一种权限

锁可以做什么?

对于一个资源加锁后,每次只能有一个线程对该资源进行操作,当该线程操作结束后,才会解锁。

解锁之后,所有的线程获得竞争此资源的机会。

什么情况下需要加锁?

  • 读读 不需要加锁
  • 写写 需要加锁
  • 读写 需要加锁

加锁的两种方式(synchronized关键字与Lock对象)

第一种:synchronized关键字

  • 方法前加synchronized关键字

    • 功能:线程进入用synchronized声明的方法时就上锁,方法执行完自动解锁,锁的是当前类的对象
    • 调用synchronized声明的方法一定是排队运行的
    • 当A线程 调用object对象的synchronized声明的X方法时
      • B线程可以调用其他非synchronized声明的方法
      • B线程不能调用其他synchronized声明的非X方法
  • synchronized锁重入

    • 锁重入的概念:自己可以重复获得自己的内部锁。即synchronized声明的方法,可以调用本对象的其他synchronized方法。
    • 锁重入支持继承的环境,即子类的synchronized方法也可以调用父类的synchronized方法。
  • synchronized同步代码块

    • synchronized关键字与synchronized代码块的区别

      • synchronized声明的方法是将当前对象作为锁
      • synchronized代码块是将任意对象作为锁
    • 当两个线程访问同一个对象的synchronized代码块时,只有一个线程可以得到执行,另一个线程只能等待当前线程执行完才能执行。

      • 一半同步,一半异步
        • 不在synchronized代码块中就是异步执行,在synchronized代码块中就是同步执行

下面对“一半同步,一半异步”进行代码验证

  • 创建项目ltl0002 ,文件Task的代码如下:
package ltl0002;public class Task {public void doTask(){for (int i = 0; i < 100; i++) {System.out.println("no synchronized ThreadName = " + Thread.currentThread().getName() + " i = " + (i+1));}synchronized (this){for (int i = 0; i < 100; i++) {System.out.println("synchronized ThreadName = " + Thread.currentThread().getName() + " i = " + (i+1));}}}
}
  • 两个线程类代码
package ltl0002;public class MyThread1 implements Runnable{private Task task = new Task();public MyThread1(Task task){this.task = task;}@Overridepublic void run() {task.doTask();}
}
package ltl0002;public class MyThread2 implements Runnable{private Task task = new Task();public MyThread2(Task task){this.task = task;}@Overridepublic void run() {task.doTask();}
}

文件Run.java代码如下:

package ltl0002;public class Run {public static void main(String[] args) {Task task = new Task();MyThread1 myThread1 = new MyThread1(task);MyThread2 myThread2 = new MyThread2(task);Thread tr1 = new Thread(myThread1);Thread tr2 = new Thread(myThread2);tr1.start();tr2.start();}}

程序运行结果如图所示

进入synchronized代码块之后,排队运行,运行结果如图所示

在第一张图我们可以看到,线程0 和 1交叉输出,说明是异步进行,而在第二张图可以看出线程0运行完之后,线程1才运行,说明它们是同步运行,验证完毕。

  • 现有三个线程,线程一对num进行修改,线程二三对num进行读取,如何可以实现,线程一与线程二三同步执行,而线程二三异步执行呢?

    现在创建项目ltl0003进行测试,Number文件代码如下

package ltl0003;
/*** @author liTianLu* @Date 2022/4/23 15:53* @purpose 成员变量有int num,以及get set方法*/
public class Number {private int num;private boolean change = false;public int getNum() {return num;}public void setNum(int num) {this.num = num;}public boolean isChangeing(){return change;}public void setChange(boolean change) {this.change = change;}
}

两个线程类的代码如下:

package ltl0003;
/*** @author liTianLu* @Date 2022/4/23 15:36* @purpose 更改num的值*/
public class MyThread01 implements Runnable{static int num = 0;Number number;public MyThread01(Number num ){this.number = num ;}@Overridepublic void run() {synchronized (this){number.setChange(true);for (int i = 0; i < 10000; i++) {number.setNum(num++);}number.setChange(false);}}
}
package ltl0003;import static java.lang.Thread.sleep;
/*** @author liTianLu* @Date 2022/4/23 15:35* @purpose 读取num的值*/
public class MyThread02 implements Runnable{Number number;public MyThread02(Number num ){this.number = num ;}@Overridepublic void run() {for (int i = 0; i < 1000 ; i++) {//如果number正在更改,就休眠1mswhile(number.isChangeing()){try {sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+"的输出为: num = " + number.getNum());}}}

主函数文件Run代码如下:

package ltl0003;
/*** @author liTianLu* @Date 2022/4/23 15:15* @purpose 解决锁问题 线程一对num进行修改,线程二三对num进行读取,此代码要实现:线程一与线程二三同步执行,而线程二三异步执行。*/
public class Run {public static void main(String[] args) {Number number = new Number();number.setNum(0);MyThread01 myThread01 = new MyThread01(number);MyThread02 myThread02 = new MyThread02(number);Thread tr1 = new Thread(myThread01);Thread tr2 = new Thread(myThread02);Thread tr3 = new Thread(myThread02);tr1.start();tr2.start();tr3.start();}
}

实验结果如图所示

我们发现,线程2/3执行的时候,线程1已经执行完毕,且线程2、3异步进行。

第二种:Lock对象的使用

  • ReentrantLock类可以达到与synchronized同样的效果。
  • 用法:
ReentrantLock lock = new ReentrantLock (); 
lock.lock();//加锁
lock.unlock();//解锁//使用try catch finally 可以确保finally 中的代码执行,在finally中解锁
try{while(true){lock.lock ();//操作代码}
}catch (Exception e) {e.printStackTrace();
}finally {lock.unlock ();
}

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

相关文章

java线程和锁

锁&#xff0c;是计算机系统中非常常见的技术&#xff0c;实现线程对资源的独占&#xff0c;防止对资源的并发读写造成错误&#xff0c;本文通过从线程&#xff0c;线程状态&#xff0c;到java提供的锁基础&#xff0c;基础的复盘一下线程和锁线程 计算机系统中&#xff0c;经…

Java线程里的14种锁

参考资料: 不可不说的Java“锁”事 java多线程的15种锁 以下内容是参考上面两片文章写出的粗略总结, 想要细究可以看上面两位大佬写的文章, 由于是参考着写的, 所以有很多地方相同, 如果有侵权或不妥的地方还请联系删除. 一. 线程是否同步资源? 1. 悲观锁 : 同步 每次拿数…

Java多线程 各种锁(一篇全搞懂)

Java多线程 锁 文章目录 Java多线程 锁1、乐观锁与悲观锁2、公平锁与非公平锁3、可重入锁与不可重入锁4、独享锁与共享锁5、自旋锁 VS 适应性自旋锁6、无锁 、 偏向锁、量级锁 和 重量级锁&#xff08;难点&#xff09; 1、乐观锁与悲观锁 &#xff08;1&#xff09;悲观锁 对…

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

卖票案例 同步代码块解决数据安全问题 同步方法解决数据安全问题 同步方法的格式&#xff1a; 同步方法和同步方法块的区别&#xff1a; 同步静态方法 Lock锁 卖票案例 某电影院目前正在上映国产大片&#xff0c;共有30张票&#xff0c;而它有3个窗口卖票&#xff0c;请…

浅析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…