前言
线程安全是并发编程中的重要关注点。
造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。
为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行。
在 Java 中,关键字 Synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作)。
下面来一起探索Synchronized的基本使用、实现机制。
用法
两类锁:
对象锁:包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)。
类锁:指Synchronized修饰静态的方法或指定锁为Class对象。
当一个线程试图访问同步代码块时,它首先必须得到锁,而退出或抛出异常时必须释放锁。
-
给普通方法加锁时,上锁的对象是 this;
-
给静态方法加锁时,锁的是 class 对象;
-
给代码块加锁,可以指定一个具体的对象作为锁。
代码示例如下:
public class SynchronizedTest {
/**
* 修饰静态方法, 等同于下面注释的方法
*/
public synchronized static void test1() {
System.out.println("月伴飞鱼");
}
// public static void test1() {
// synchronized (SynchronizedTest.class){
// System.out.println("月伴飞鱼");
// }
// }
/**
* 修饰实例方法, 等同于下面注释的方法
*/
public synchronized void test2(){
System.out.println("月伴飞鱼");
}
// public void test2(){
// synchronized (this){
// System.out.println("月伴飞鱼");
// }
// }
/**
* 修饰代码块
*/
public void test3(){
synchronized (this){
System.out.println("月伴飞鱼");
}
}
}
多线程访问同步方法的几种情况:
两个线程同时访问一个对象的同步方法。
由于同步方法锁使用的是this对象锁,同一个对象的this锁只有一把,两个线程同一时间只能有一个线程持有该锁,所以该方法将会串行运行。
两个线程访问的是两个对象的同步方法。
由于两个对象的this锁互不影响,Synchronized将不会起作用,所以该方法将会并行运行。
两个线程访问的是Synchronized的静态方法。
Synchronized修饰的静态方法获取的是当前类模板对象的锁,该锁只有一把,无论访问多少个该类对象的方法,都将串行执行。
同时访问同步方法与非同步方法
非同步方法不受影响。
访问同一个对象的不同的普通同步方法。
由于this对象锁只有一个,不同线程访问多个普通同步方法将串行运行。
同时访问静态Synchronized和非静态Synchronized方法
静态Synchronized方法的锁为class对象的锁,非静态Synchronized方法锁为this的锁,它们不是同一个锁,所以它们将并行运行。
使用优化
大家在使用synchronized关键字的时候ÿ