🌈hello,你好鸭,我是Ethan,西安电子科技大学大三在读,很高兴你能来阅读。
✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。
🏃人生之义,在于追求,不在成败,勤通大道。加油呀!
🔥个人主页:Ethan Yankang
🔥推荐:史上最强八股文||一分钟看完我的几百篇博客
🔥温馨提示:划到文末发现专栏彩蛋 点击这里直接传送
🔥本篇概览:详细讲解了竞态条件的解决方法。🌈⭕🔥
【计算机领域一切迷惑的源头都是基本概念的模糊,算法除外】
目录
🌈序言:
JAVA基础必序扎实的一批,此关不过,啥都没有。今日得《JAVA核心技术·卷I》之良品辅助,应按本书学之习之,时时复习,长此以往必能穿魂入脉,习得大功。
感谢Cay S. Horstmann给世界留下如此优美的作品。
对于一个强烈想完全掌握JAVA的技术宅来说,JAVA的XXX万万不能放过,这些基础的概念例程都值得细细体味的,千万别觉得都是文字,浪费时间,记住——别违背科学发展的客观规律。别一味地赶进度以满足自己学的都么快的虚荣心,自欺欺人,要老老实实的走好每一步。
上一篇文章详细讲解了竞态条件的出现原因,建议先将这部分知识掌握之后再来学习本篇内容,点击查看。
🔥《JAVA核心技术卷·I》——第十二章5——同步1——竞态条件的出现原因——《并发》-CSDN博客
两种解决机制防止并发访问代码块。ReentrantLock&synchronized。
下面一一讲解,先讲述ReentrantLock会更加让人容易理解后面的synchronized。不过在这之前还的讲解一下临界区的知识。
🌈ReentrantLock
🌈临界区
计算机中的临界区是指访问共享资源的程序片段,在这段程序片段中,进程或线程可能会改变共享资源的状态。
临界区的特点包括:
- 共享性:涉及对共享资源的访问和操作。
- 排他性:在同一时刻,只能有一个进程或线程处于临界区内访问共享资源,以避免数据不一致和错误。
例如,多个线程同时操作一个全局变量进行修改时,对这个全局变量的修改操作所在的代码区域就是临界区。
再比如,多个进程同时向一个文件写入数据时,进行文件写入操作的代码部分就是临界区。
临界区的正确管理对于保证多线程或多进程程序的正确性和稳定性至关重要。
🌈锁对象
用 ReentrantLock保护代码块的基本结构如下:
示例代码如下:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static int count = 0;
private static ReentrantLock lock = new ReentrantLock();
public static void increment() {
lock.lock();
try {
count++;
System.out.println("当前线程: " + Thread.currentThread().getName() + ", 计数值: " + count);
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
// 创建多个线程并执行 increment 方法
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
increment();
}
});
thread1.start();
thread2.start();
}
}
上面的被ReentrantLock包围起来的结构可以实现确保任何一个时刻只有一个线程进入临界区(计算机中的临界区是指访问共享资源的程序片段)。
🌈警告
🌈修改后的代码
class Bank {
定义重入锁
private ReentrantLock bankLock = new ReentrantLock();
private final double[] accounts;
Bank(int n, double initialBalance) {
this.accounts = new double[n];
Arrays.fill(accounts, initialBalance);
}
public void transfer(int from, int to, double amount) {
上锁
bankLock.lock();
try {
if (accounts[from] < amount) return;
System.out.print("线程信息:" + Thread.currentThread());
System.out.print("\t线程I D:" + Thread.currentThread().getId());
accounts[from] -= amount;
System.out.printf(" %10.2f from %d to %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Total Balance:%10.2f%n", getTotalBalance());
} finally {
放锁
bankLock.unlock();
}
}
🌈运行结果:
🌈注意点:对象锁而非类锁
注意点,此处的tranfers是对象级别的方法,而非类级别(改为static即可是类级别),所以ReentrantLock是对象锁,而非类锁。
注意每个Bank对象都有自己的ReentrantLock对象。如果两个线程试图访问同一个Bank对象,那么锁可以用来保证串行化访问。不过,如果两个线程访问不同的Bank对象,每个线程会得到不同的锁对象,两个线程都不会阻塞,无需用锁。本该如此,因为线程在操纵不同的Bank实例时,线程之间不会相互影响。
【各自线程工作完好】
🌈可重入锁
这个锁称为重入(reentrant)锁,因为线程可以反复获得已拥有的锁。锁有一个持有计数(holdcount)来跟踪对lock方法的嵌套调用。
线程每一次调用lock后都要调用unlock来释放锁。由于这个特性,被一个锁保护的代码可以调用另一个使用相同锁的方法。类似于出栈的操作有序进行。
🌈API
💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖
热门专栏推荐
🌈🌈计算机科学入门系列 关注走一波💕💕
🌈🌈CSAPP深入理解计算机原理 关注走一波💕💕
🌈🌈微服务项目之黑马头条 关注走一波💕💕
🌈🌈redis深度项目之黑马点评 关注走一波💕💕
🌈🌈JAVA面试八股文系列专栏 关注走一波💕💕
🌈🌈JAVA基础试题集精讲 关注走一波💕💕
🌈🌈代码随想录精讲200题 关注走一波💕💕
总栏
🌈🌈JAVA基础要夯牢 关注走一波💕💕
🌈🌈JAVA后端技术栈 关注走一波💕💕
🌈🌈JAVA面试八股文 关注走一波💕💕
🌈🌈JAVA项目(含源码深度剖析) 关注走一波💕💕
🌈🌈计算机四件套 关注走一波💕💕
🌈🌈数据结构与算法 关注走一波💕💕
🌈🌈必知必会工具集 关注走一波💕💕
🌈🌈书籍网课笔记汇总 关注走一波💕💕
📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤收藏✅ 评论💬,大佬三连必回哦!thanks!!!
📚愿大家都能学有所得,功不唐捐!