synchronized
关键字可以保证并发编程的三大特性:原子性、可见性、有序性;而volatile
关键字只能保证可见性和有序性,不能保证原子性,也称为是轻量级的synchronized
。
synchronized
synchronized
可以实现的锁:
悲观锁:每次访问共享资源时都会上锁。
非公平锁:线程获取锁的顺序并不一定是按照线程阻塞的顺序。
可重入锁:即已经获取锁的线程可以再次获取锁。
独占锁(排他锁):该锁只能被一个线程所持有,其他线程均被阻塞。
使用方式
修饰普通同步方法
public class Main {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread1 = Thread.ofVirtual().unstarted(test::run);
Thread thread2 = Thread.ofVirtual().unstarted(test::run);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Test.i);
}
}
class Test {
public static Integer i = 0;
public void increment() {
i++;
}
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
}
15794
不使用 synchronized
时,显然会出现线程不安全的问题。
public class Main {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread1 = Thread.ofVirtual().unstarted(test::run);
Thread thread2 = Thread.ofVirtual().unstarted(test::run);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Test.i);
}
}
class Test {
public static Integer i = 0;
public synchronized void increment() {
i++;
}
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
}
20000
使用 synchronized
限定后,同一时间只有一个线程能执行 test
对象中被 synchronized
限定的方法。
修饰静态方法
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = Thread.ofVirtual().unstarted(new Test()::run);
Thread thread2 = Thread.ofVirtual().unstarted(new Test()::run);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Test.i);
}
}
class Test {
public static Integer i = 0;
public synchronized void increment() {
i++;
}
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
}
15528
increment()
虽然也使用 synchronized
修饰了,但是因为两次 new Test()
操作建立的是两个不同的对象,也就是说存在两个不同的对象锁,线程 thread1
和 thread2
使用的是不同的对象锁,所以不能保证线程安全。那这种情况应该如何解决呢?因为每次创建的实例对象都是不同的,而类对象却只有一个,所以将 synchronized
作用于类对象,即用 synchronized
修饰静态方法即可。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = Thread.ofVirtual().unstarted(new Test()::run);
Thread thread2 = Thread.ofVirtual().unstarted(new Test()::run);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Test.i);
}
}
class Test {
public static Integer i = 0;
public static synchronized void increment() {
i++;
}
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
}
20000
修饰同步方法代码块
如果某些情况下,整个方法体比较大,需要同步的代码只是一小部分,如果直接对整个方法体进行同步,会使得代码性能变差,这时只需要对一小部分代码进行同步即可。
public class Main {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread1 = Thread.ofVirtual().unstarted(test::run);
Thread thread2 = Thread.ofVirtual().unstarted(test::run);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Test.i + ", " + Test.j);
}
}
class Test {
public static Integer i = 0;
public static Integer j = 0;
public void increment() {
synchronized (this) { // or sychronized (Test.class) {}
i++;
}
j++;
}
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
}
20000, 18821
synchronized
和 Lock
的区别
Lock
是显式锁,需要手动开启和关闭。synchronized
是隐式锁,可以自动释放锁。Lock
是一个接口,是 JDK 实现的。synchronized
是一个关键字,是依赖 JVM 实现的。Lock
是可中断锁,synchronized
是不可中断锁,需要线程执行完才能释放锁。发生异常时,
Lock
不会主动释放占有的锁,必须通过unlock()
进行手动释放,因此可能引发死锁。synchronized
在发生异常时会自动释放占有的锁,不会出现死锁的情况。Lock
可以判断锁的状态,synchronized
不可以判断锁的状态。Lock
实现锁的类型是可重入锁、公平锁。synchronized
实现锁的类型是可重入锁,非公平锁。Lock
适用于大量同步代码块的场景,synchronized
适用于少量同步代码块的场景。
volatile
volatile
不保证原子性,只保证可见性以及禁止指令重排(有序性)。
public class Main {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread1 = Thread.ofVirtual().unstarted(test::run);
Thread thread2 = Thread.ofVirtual().unstarted(test::run);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Test.i);
}
}
class Test {
public static volatile Integer i = 0;
public void increment() {
i++;
}
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
}
12403