一、概念
利用锁机制实现线程同步,synchronized关键字的底层交由了JVM通过C++来实现
Java中的锁有两大特性:
- 互斥性
- 同一时间,只允许一个线程持有某个对象锁。
- 可见性
- 锁释放前,线程对变量的修改,后面获得锁的线程可见。
可见性
JMM关于synchronized的两条规定:
-
线程解锁前,必须把共享变量的最新值刷新到主内存中
-
线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值(注意:加锁与解锁需要是同一把锁)
通过以上两点,可以看到synchronized能够实现可见性。
二、用法
synchronized修饰位置与锁的关系:
- 同步方法 —— 对象锁,当前实例对象
- 静态同步方法 —— 类对象锁,当前对象的Class对象
- 同步方法块 —— 对象锁,synchonized括号里配置的对象
public class SynchronizedDemo {
private static int m = 0;
private Object obj = new Object();
/**
* 修饰非静态方法
*/
public synchronized void m1() {
sleep(2);
m++;
}
/**
* 修饰静态方法
*/
public synchronized static void m2() {
sleep(2);
m++;
}
/**
* 同步代码块,对对象加锁
*/
public synchronized void m3() {
synchronized (this) {
sleep(2);
m++;
}
}
public synchronized void m4() {
synchronized (obj) {
sleep(2);
m++;
}
}
/**
* 同步代码块,对类加锁
*/
public synchronized void m5() {
synchronized (SynchronizedDemo.class) {
sleep(2);
m++;
}
}
public static void sleep(int second) {
try {
Thread.sleep(second * 1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、面试问题
同一个对象在A、B两个线程中分别访问该对象的两个同步方法writer和reader,是否会产生互斥?
class LockDemo{
int a = 0;
public synchronized void writer(){
sleep(10);
a++;
}
public synchronized void reader(){
int i = a;
}
public static void main(String[] args) {
Test test = new Test();
new Thread(() -> {
test.writer();
}).start();
sleep(1);
new Thread(() -> {
test.reader();
}).start();
}
}
答案:会。因为synchronized修饰的是方法,锁是对象锁
,默认当前的对象作为锁的对象。只有当A释放锁之后,B才会获得对象的锁。
(1)如果是换成是不同对象呢?
不会互斥,因为锁的是对象
,而不是方法
。
(2)如果writer、reader方法加上static修饰,两个线程中,类直接调用两个方法呢?
会互斥,因为锁的是Class对象。
(3)如果writer方法用static修饰,reader方法不用呢?
不会互斥。因为一个是对象锁,一个是Class对象锁,锁的类型不同。