主要讲解synchronized的应⽤⽅式和内存语义。
前⾔
看这篇⽂章前,建议⼤家先看我前⾯的⽂章《Java并发编程系列1-基础知识》,否则⾥⾯的相关知识看不懂,特别 是并发编程相关的可⻅性、有序性,以及内存模型JMM等。
在Java中,关键字synchronized可以保证在同⼀个时刻,只有⼀个线程可以执⾏某个⽅法或者某个代码块(主要是 对⽅法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外⼀个重要的作⽤, synchronized可保证⼀个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可⻅性,完全可以替代 Volatile功能)。
一、synchronized的三种应⽤⽅式
synchronized关键字最主要有以下3种应⽤⽅式,下⾯分别介绍:
- 修饰实例⽅法,作⽤于当前实例加锁,进⼊同步代码前要获得当前实例的锁;
- 修饰静态⽅法,作⽤于当前类对象加锁,进⼊同步代码前要获得当前类对象的锁;
- 修饰代码块,指定加锁对象,对给定对象加锁,进⼊同步代码库前要获得给定对象的锁。
1.1 synchronized作⽤于实例⽅法
所谓的实例对象锁就是⽤synchronized修饰实例对象中的实例⽅法,注意是实例⽅法不包括静态⽅法,如下:
public class AccountingSync implements Runnable {
//共享资源(临界资源)
static int i = 0;
// synchronized 修饰实例⽅法
public synchronized void increase() {
i ++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++){
increase();
}
}
public static void main(String args[]) throws InterruptedException {
AccountingSync instance = new AccountingSync();
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("static, i output:" + i);
}
}
/**
* 输出结果:
* static, i output:2000000
*/
如果在函数increase()前不加synchronized,因为i++不具备原⼦性,所以最终结果会⼩于2000000,具体分析可以 参考⽂章《Java并发编程系列2-volatile》。下⾯这点⾮常重要:
⼀个对象只有⼀把锁,当⼀个线程获取了该对象的锁之后,其他线程⽆法获取该对象的锁,所以⽆法访问该 对象的其他synchronized实例⽅