|NO.Z.00102|——————————|BigDataEnd|——|Java&多线程.V14|——|Java.v14|线程同步机制|概念由来|同步代码块实现线程同步方式|
一、线程同步机制的概念和由来
### --- 线程同步机制的概念和由来
——> [线程同步机制的概念和由来]
——> [同步代码块实现线程同步的方式一]
——> [同步方法实现线程同步的方式二]
### --- 线程同步机制(重点)
### --- 基本概念
——> 当多个线程同时访问同一种共享资源时,可能会造成数据的覆盖等不一致性问题,此时就需要对线程之间进行通信和协调,该机制就叫做线程的同步机制。
——> 多个线程并发读写同一个临界资源时会发生线程并发安全问题。
——> 异步操作:多线程并发的操作,各自独立运行。
——> 同步操作:多线程串行的操作,先后执行的顺序。
### --- 解决方案
——> 由程序结果可知:当两个线程同时对同一个账户进行取款时,导致最终的账户余额不合理。
——> 引发原因:线程一执行取款时还没来得及将取款后的余额写入后台,线程二就已经开始取款。
——> 解决方案:让线程一执行完毕取款操作后,再让线程二执行即可,将线程的并发操作改为串行操作。
——> 经验分享:在以后的开发尽量减少串行操作的范围,从而提高效率。
### --- 实现方式
——> 在Java语言中使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性,具体方式如下:
——> 使用同步代码块的方式实现部分代码的锁定,格式如下:
——> synchronized(类类型的引用) {
——> 编写所有需要锁定的代码;
——> }
——> 使用同步方法的方式实现所有代码的锁定。
——> 直接使用synchronized关键字来修饰整个方法即可
——> 该方式等价于:
——> synchronized(this) { 整个方法体的代码 }
二、编程代码
package com.yanqi.task18;
import java.util.concurrent.locks.ReentrantLock;
public class AccountRunnableTest implements Runnable {
private int balance; // 用于描述账户的余额
private Demo dm = new Demo();
private ReentrantLock lock = new ReentrantLock(); // 准备了一把锁
public AccountRunnableTest() {
}
public AccountRunnableTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public /*synchronized*/ void run() {
// 开始加锁
lock.lock();
// 由源码可知:最终是account对象来调用run方法,因此当前正在调用的对象就是account,也就是说this就是account
//synchronized (this) { // ok
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
//synchronized (dm) { // ok
//synchronized (new Demo()) { // 锁不住 要求必须是同一个对象
// 1.模拟从后台查询账户余额的过程
int temp = getBalance(); // temp = 1000 temp = 1000
// 2.模拟取款200元的过程
if (temp >= 200) {
System.out.println("正在出钞,请稍后...");
temp -= 200; // temp = 800 temp = 800
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走您的钞票!");
} else {
System.out.println("余额不足,请核对您的账户余额!");
}
// 3.模拟将最新的账户余额写入到后台
setBalance(temp); // balance = 800 balance = 800
//}
lock.unlock(); // 实现解锁
}
public static void main(String[] args) {
AccountRunnableTest account = new AccountRunnableTest(1000);
//AccountRunnableTest account2 = new AccountRunnableTest(1000);
Thread t1 = new Thread(account);
Thread t2 = new Thread(account);
//Thread t2 = new Thread(account2);
t1.start();
t2.start();
System.out.println("主线程开始等待...");
try {
t1.join();
//t2.start(); // 也就是等待线程一取款操作结束后再启动线程二
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终的账户余额为:" + account.getBalance()); // 600 800
}
}
class Demo{}
三、编译打印
D:\JAVA\jdk-11.0.2\bin\java.exe "-javaagent:D:\IntelliJIDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=51045:D:\IntelliJIDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\NO.Z.10000——javaproject\NO.H.00001.javase\javase\out\production\javase com.yanqi.task18.AccountRunnableTest
主线程开始等待...
线程Thread-0已启动...
正在出钞,请稍后...
请取走您的钞票!
线程Thread-1已启动...
正在出钞,请稍后...
请取走您的钞票!
最终的账户余额为:600
Process finished with exit code 0
Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
——W.S.Landor
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通