同步代码块
作用
把访问共享资源的核心代码给上锁,以此保证线程安全。
格式
synchronized(同步锁){
访问共享资源的核心代码
}
原理
多个线程在执行这个代码块之前必须竞争获取指定对象的锁。因为互斥访问:同一时间只有一个线程能持有锁并执行同步块内的代码,其他线程会被阻塞,直到锁被释放。
同步锁的条件
唯一性:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象)。因为如果不是唯一的话,那么就可能多个线程可以同时执行核心代码块,违背了synchronized关键字的原理
锁对象的使用规范
对于实例方法建议使用this作为锁对象
不能随便取一个唯一的对象。因为它会阻止你同时操作两个不同的账户,但是我们只需要阻止同时操作同一个账户。所以我们应该用this作为锁对象。
为什么要用this作为锁对象,因为 synchronized (this) {}这行代码是在public void drawAccount() {}这个方法里的,所以this可以拿到调用drawAccount这个方法的对象。
那么this
就代表了当前对象实例的引用。在实例方法中使用 synchronized (this)
,意味着锁与当前对象实例绑定。每个对象实例都有自己独立的锁,因此不同实例之间的操作不会互相阻塞。
优势:确保同一实例的线程安全,又可以支持不同实例的并发操作:
public class threadDemo1 {
public static void main(String[] args) {
//目标:线程同步的方式一:同步代码块
//1、设计一个账户类,用于创建小明和小红的共同账户(10w元0
Account acc = new Account("ICBC-001", 100000);
//2、设计线程类,创建小明和小红两个线程,模拟小明和小红同时去同一个账户取10万
new DrawThread("小明",acc).start();
new DrawThread("小红",acc).start();
}
}
public void drawAccount() {
//拿到当前线程
String name = Thread.currentThread().getName();
synchronized (this) {
//判断余额是否足够
if (money >= 100000) {
System.out.println(name + "取款成功,吐出钞票"+money+"元");
money -= 100000;
System.out.println(name + "取款成功,金额剩余"+money+"元");
} else {
System.out.println(name + "取款失败,余额不足");
}
}
}
public class DrawThread extends Thread {
private Account acc;
public DrawThread(String name, Account acc){
super(name);
this.acc = acc;
}
@Override
public void run() {
acc.drawAccount();
}
}
对于静态方法建议使用字节码(类名.class)对象作为锁对象
唯一性:每个加载到JVM中的类都有一个唯一的 Class 对象。通过使用 类名.class 作为锁,可以确保锁的唯一性。所以无论有多少个该类的实例,所有对该类静态资源的访问都会受到这同一个锁的保护。