当前位置: 首页 > news >正文

线程锁和线程同步

 线程锁和线程同步

线程锁的概念

线程锁是一种用于控制多个线程对共享资源访问的机制,目的是确保在同一时刻,只有一个线程能够访问共享资源,避免出现数据不一致、竞态条件等问题。就像在生活中,一把钥匙对应一扇门,同一时间只有拿到钥匙的人能进入门内。

synchronized关键字

synchronized是 Java 内置的用于实现线程同步的关键字,它可以应用在以下几个方面:

1. 修饰实例方法

synchronized修饰一个实例方法时,锁对象是当前对象(this)。这意味着在同一时刻,只有一个线程能够进入该实例方法进行操作。

class Counter {private int count = 0;// 修饰实例方法,锁对象是当前Counter实例public synchronized void increment() {count++;}public int getCount() {return count;}
}public class Main1 {public static void main(String[] args) {Counter counter = new Counter();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("最终计数: " + counter.getCount());}
}

修饰静态方法

synchronized修饰静态方法时,锁对象是该类的Class对象,因为静态方法属于类,所有该类的实例共享同一个Class对象锁。

class StaticCounter {private static int count = 0;// 修饰静态方法,锁对象是StaticCounter类的Class对象public static synchronized void increment() {count++;}public static int getCount() {return count;}
}public class Main2 {public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {StaticCounter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {StaticCounter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("最终计数: " + StaticCounter.getCount());}
}

修饰代码块

可以使用synchronized关键字修饰代码块,显式指定锁对象。这比修饰方法更加灵活,可以只对关键代码部分进行同步,提高程序性能。

class BankAccount {private int balance = 1000;public void transfer(BankAccount other, int amount) {// 这里使用this和other作为锁对象,保证转账操作的原子性synchronized (this) {synchronized (other) {if (this.balance >= amount) {this.balance -= amount;other.balance += amount;}}}}public int getBalance() {return balance;}
}public class Main3 {public static void main(String[] args) {BankAccount account1 = new BankAccount();BankAccount account2 = new BankAccount();Thread thread = new Thread(() -> {account1.transfer(account2, 500);});thread.start();try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("account1余额: " + account1.getBalance());System.out.println("account2余额: " + account2.getBalance());}
}

synchronized的工作原理

当一个线程访问被synchronized修饰的方法或代码块时:

  1. 首先会检查该对象的锁标志位。如果锁标志位为 0,表示没有线程持有锁,那么该线程会获取锁,将锁标志位设置为 1,并进入同步代码。
  2. 如果锁标志位为 1,表示已经有其他线程持有锁,当前线程会被阻塞,进入该对象的等待队列,直到持有锁的线程释放锁(执行完同步代码块或方法,或者发生异常),然后被唤醒并重新尝试获取锁。

注意事项

  • 性能开销:虽然synchronized能有效解决线程安全问题,但它会带来一定的性能开销,因为线程的阻塞和唤醒都需要消耗系统资源。因此,要避免过度使用,尽量只对关键代码进行同步。
  • 死锁问题:在使用synchronized修饰多个对象的代码块时,如果线程获取锁的顺序不一致,可能会导致死锁。比如线程 A 持有对象 X 的锁,等待获取对象 Y 的锁,而线程 B 持有对象 Y 的锁,等待获取对象 X 的锁,此时两个线程都无法继续执行。
  • 我们重点要理解,synchronized锁的是什么。两个线程竞争同⼀把锁,才会产生阻塞等待。 两个线程分别尝试获取两把不同的锁,不会产⽣竞争。
http://www.lryc.cn/news/581413.html

相关文章:

  • Servlet学习
  • Spring--循环依赖以及三级缓存详解
  • Chat Model API
  • Altium Designer使用教程 第一章(Altium Designer工程与窗口)
  • Eureka和Nacos都可以作为注册中心,它们之间的区别
  • Java类变量(静态变量)
  • 【论文】微服务架构下分布式事务一致性解决方案设计与实践
  • 《数据维度的视觉重构:打造交互式高维数据可视化的黄金法则》
  • Java教程——深入学习guava之并发编程
  • 如何使用backtrace定位Linux程序的崩溃位置
  • Python练习Day1
  • 【C语言刷题】第十一天:加量加餐继续,代码题训练,融会贯通IO模式
  • 双倍硬件=双倍性能?TDengine线性扩展能力深度实测验证!
  • 类(JavaBean类)和对象
  • BM6 判断链表中是否有环(牛客)
  • Linux安装java后没法运行
  • 西门子PLC博图软件学习(一)
  • 手写 Vue 中虚拟 DOM 到真实 DOM 的完整过程
  • .NET9 实现排序算法(MergeSortTest 和 QuickSortTest)性能测试
  • LinkedList 链表数据结构实现 (OPENPPP2)
  • 前端面试专栏-算法篇:18. 查找算法(二分查找、哈希查找)
  • AI智能体革命:从对话机器到自主决策的进化之路 **——当AI长出“手和脑”,一场人机协作范式转移正在发生
  • AI小智项目全解析:软硬件架构与开发环境配置
  • 图灵完备之路(数电学习三分钟)----解码器
  • Pytest 测试发现机制详解:自动识别测试函数与模块
  • 理想汽车6月交付36279辆 第二季度共交付111074辆
  • 比较两个csv文件的内容是否一致
  • Python 机器学习核心入门与实战进阶 Day 3 - 决策树 随机森林模型实战
  • HTML初学者第三天
  • centos 7.6安装mysql8