文章目录
Synchronized的三种应用方式
-
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁;
-
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。
-
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象。
-
Synchronized
也叫互斥锁,顾名思义:能到达到互斥访问目的的锁。
Synchronized的实现
基本实现
JVM
是通过进入、退出对象监视器(Monitor
)来实现对方法、同步块的同步的。
重点:
synchronized
同步块使用了monitor.enter
和monitor.exit
指令实现同步,
这两个指令,本质上都是对一个对象的监视器(monitor
)进行获取,这个过程是排他的,也就是说同一时刻只能有一个线程获取到由synchronized
所保护对象的监视器。
线程执行到monitor.enter
指令时,会尝试获取对象所对应的monitor
所有权,也就是尝试获取对象的锁,而执行monitor.exit
,就是释放monitor
的所有权。
而对于没有获取到锁的线程将会阻塞到方法入口处,直到获取锁的线程 monitor.exit
之后才能尝试继续获取锁。
Monitor
Monitor是一个同步工具,它内置于每一个Object
对象中,相当于一个许可证。拿到许可证即可以进行操作,没有拿到则需要阻塞等待。
++图解:++
小知识:wait和notify为什么需要在synchronized里面?
都是Object的方法,换句话说,就是每个类里面都有这些方法:
-
Object.wait():释放当前对象锁,并进入阻塞队列
-
Object.notify():唤醒当前对象阻塞队列里的任一线程(并不保证唤醒哪一个)
-
Object.notifyAll():唤醒当前对象阻塞队列里的所有线程
解释这个问题之前,我们先要了解几个知识点:
-
每一个对象都有一个与之对应的监视器
-
每一个监视器里面都有一个该对象的锁和一个阻塞队列和一个同步队列
解答
wait()
方法的语义有两个,一是释放当前对象锁,另一个是进入阻塞队列,可以看到,这些操作都是与监视器相关的,当然要指定一个监视器才能完成这个操作了
notify()方法也是一样的,用来唤醒一个线程,你要去唤醒,首先你得知道他在哪儿,所以必须先找到该对象,也就是获取该对象的锁,当获取到该对象的锁之后,才能去该对象的对应的阻塞队列去唤醒一个线程。值得注意的是,只有当执行唤醒工作的线程离开同步块,即释放锁之后,被唤醒线程才能去竞争锁。
notifyAll()方法和notify()一样,只不过是唤醒等待队列中的所有线程
因wait()而导致阻塞的线程是放在阻塞队列中的,因竞争失败导致的阻塞是放在同步队列中的,notify()/notifyAll()实质上是把阻塞队列中的线程放到同步队列中去
附:Synchronized的三种应用方式的代码案例
同步代码块:在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
使用:
public class TestSynchronized {
public static void main(String[] args) {
//创建票对象
Ticket ticket = new Ticket();
//创建3个窗口
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
t1.start();
t2.start();
t3.start();
}
static class Ticket implements Runnable {
//共100票
int ticket = 100;
//定义锁对象
Object lock = new Object();
@Override
public void run() {
//模拟卖票
while (true) {
synchronized (lock) {
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
}
}
}
同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
使用:
public class TestSynchronized1 {
public static void main(String[] args) {
//创建票对象
Ticket ticket = new Ticket();
//创建3个窗口
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
t1.start();
t2.start();
t3.start();
}
public static class Ticket implements Runnable {
//共100票
int ticket = 100;
//定义锁对象
Object lock = new Object();
@Override
public void run() {
//模拟卖票
while(true){
//同步方法
method();
}
}
//同步方法,锁对象this
public synchronized void method(){
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
}
静态同步方法: 在方法声明上加上static synchronized
public static synchronized void method(){
可能会产生线程安全问题的代码
}
静态同步方法中的锁对象是 类名.class