原文链接:Thread – 10 – synchronized的基本概念
相关文章:
Lock – 01 – synchronized的基本概念
Lock – 02 – synchronized底层实现原理
Lock – 03 – synchronized的优化
Lock – 04 – ReentrantLock底层实现原理
在 Java 多线程编程中,我们需要重点关注一个问题,那就是线程安全问题,造成线程安全的原因,有以下两点
存在共享数据 (也称为临界资源)
存在多个线程共同操作共享数据
为了解决该问题,我们需要引进这么一个方法
此时,便引入了互斥锁的概念,其有以下两点特性
互斥性
即在同一时间内只允许一个线程持有某个锁,通过这种特性来实现多线程的协调机制,这样才能确保在同一时间内只有一个线程对需要同步的代码块 (复合操作) 进行访问
互斥性也称为操作的原子性
可见性
对于 Java 而言,关键字 synchronized 满足了上述的要求
修饰实例方法
synchronized method() (对象锁)
作用于当前实例对象加锁,进入同步区域需要获得当前实例对象的锁
修饰代码块
synchronized(this) (对象锁)
synchronized(实例对象) (对象锁)
synchronized(类.class) (类锁)
作用于指定对象加锁,进入同步区域需要获得指定对象的锁
修饰静态方法
synchronized static method() (类锁)
作用于当前类对象加锁,进入同步区域需要获得当前类对象的锁
修饰实例方法
synchronized method() (对象锁)
public class SyncThread implements Runnable {
@Override
public void run() {
syncObjectMethod();
}
private synchronized void syncObjectMethod() {
System.out.println(Thread.currentThread().getName() + "_syncObjectMethod: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
try {
System.out.println(Thread.currentThread().getName() + "_syncObjectMethod_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncObjectMethod_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "thread1");
Thread thread2 = new Thread(syncThread, "thread2");
thread1.start();
thread2.start();
// thread1_syncObjectMethod: : 15:28:36
// thread1_syncObjectMethod_Start: 15:28:36
// thread1_syncObjectMethod_End: 15:28:37
// thread2_syncObjectMethod: : 15:28:37
// thread2_syncObjectMethod_Start: 15:28:37
// thread2_syncObjectMethod_End: 15:28:38
}
}
如上所示,synchronized 用于修饰实例方法,锁住的是同一个 syncThread 对象
整个实例方法内的逻辑会同步执行
因此当一个线程访问对象的同步方法时,另外访问该对象同步方法的线程将会被阻塞
修饰代码块
synchronized(this) (对象锁)
public class SyncThread implements Runnable {
@Override
public void run() {
syncThisBlock();
}
private void syncThisBlock() {
System.out.println(Thread.currentThread().getName() + "_syncThisBlock: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_syncThisBlock_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncThisBlock_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "thread1");
Thread thread2 = new Thread(syncThread, "thread2");
thread1.start();
thread2.start();
// thread1_syncThisBlock: : 15:40:24
// thread2_syncThisBlock: : 15:40:24
// thread1_syncThisBlock_Start: 15:40:24
// thread1_syncThisBlock_End: 15:40:25
// thread2_syncThisBlock_Start: 15:40:25
// thread2_syncThisBlock_End: 15:40:26
}
}
如上所示,synchronized 用于修饰代码块,锁住的是同一个 this (即 syncThread 对象)
整个实例方法内,未在同步代码块内的逻辑会异步执行,在同步代码块内的逻辑会同步执行
因此当一个线程访问对象的同步代码块时,另外访问该对象同步代码块的线程将会被阻塞
synchronized(实例对象) (对象锁)
public class SyncThread implements Runnable {
private Object lock = new Object();
@Override
public void run() {
syncObjectBlock();
}
private void syncObjectBlock() {
System.out.println(Thread.currentThread().getName() + "_syncObjectBlock: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + "_syncObjectBlock_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncObjectBlock_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "thread1");
Thread thread2 = new Thread(syncThread, "thread2");
thread1.start();
thread2.start();
// thread2_syncObjectBlock: : 16:11:44
// thread1_syncObjectBlock: : 16:11:44
// thread2_syncObjectBlock_Start: 16:11:44
// thread2_syncObjectBlock_End: 16:11:45
// thread1_syncObjectBlock_Start: 16:11:45
// thread1_syncObjectBlock_End: 16:11:46
}
}
如上所示,synchronized 用于修饰代码块,锁住的是同一个 lock 对象
此外,锁住的 lock 对象必须为同一个对象,否则就会异步执行代码块了
整个实例方法内,未在同步代码块内的逻辑会异步执行,在同步代码块内的逻辑会同步执行
因此当一个线程访问对象的同步代码块时,另外访问该对象同步代码块的线程将会被阻塞
synchronized(类.class) (类锁)
public class SyncThread implements Runnable {
@Override
public void run() {
syncClassBlock();
}
private void syncClassBlock() {
System.out.println(Thread.currentThread().getName() + "_syncClassBlock: : " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_syncClassBlock_Start: " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncClassBlock_End: " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "thread1");
Thread thread2 = new Thread(syncThread, "thread2");
thread1.start();
thread2.start();
// thread1_syncClassBlock: : 16:23:27
// thread2_syncClassBlock: : 16:23:27
// thread1_syncClassBlock_Start: 16:23:28
// thread1_syncClassBlock_End: 16:23:29
// thread2_syncClassBlock_Start: 16:23:29
// thread2_syncClassBlock_End: 16:23:30
}
}
public class SyncDemo {
public static void main(String[] args) {
Thread thread1 = new Thread(new SyncThread(), "thread1");
Thread thread2 = new Thread(new SyncThread(), "thread2");
thread1.start();
thread2.start();
// thread2_syncClassBlock: : 16:24:47
// thread1_syncClassBlock: : 16:24:47
// thread2_syncClassBlock_Start: 16:24:47
// thread2_syncClassBlock_End: 16:24:49
// thread1_syncClassBlock_Start: 16:24:49
// thread1_syncClassBlock_End: 16:24:50
}
}
如上所示,synchronized 用于修饰代码块,锁住的是同一个 class 对象
同一个类的不同对象使用类锁将会是同步的
整个实例方法内,未在同步代码块内的逻辑会异步执行,在同步代码块内的逻辑会同步执行
因此当一个线程访问对象的同步代码块时,另外访问该对象同步代码块的线程将会被阻塞
修饰静态方法
synchronized static method() (类锁)
public class SyncThread implements Runnable {
@Override
public void run() {
syncClassMethod();
}
private synchronized static void syncClassMethod() {
System.out.println(Thread.currentThread().getName() + "_syncClassMethod: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
try {
System.out.println(Thread.currentThread().getName() + "_syncClassMethod_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncClassMethod_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "thread1");
Thread thread2 = new Thread(syncThread, "thread2");
thread1.start();
thread2.start();
// thread1_syncClassMethod: : 16:30:45
// thread1_syncClassMethod_Start: 16:30:45
// thread1_syncClassMethod_End: 16:30:46
// thread2_syncClassMethod: : 16:30:46
// thread2_syncClassMethod_Start: 16:30:46
// thread2_syncClassMethod_End: 16:30:47
}
}
如上所示,synchronized 用于修饰静态方法,锁住的是同一个 class 对象
整个静态方法内的逻辑会同步执行
因此当一个线程访问对象的同步方法时,另外访问该对象同步方法的线程将会被阻塞
synchronized method() (对象锁) 与 synchronized(this) (对象锁) 进行比较
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
syncThisBlock();
} else if (threadName.startsWith("B")) {
syncObjectMethod();
}
}
private void syncThisBlock() {
System.out.println(Thread.currentThread().getName() + "_syncThisBlock: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_syncThisBlock_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncThisBlock_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
private synchronized void syncObjectMethod() {
System.out.println(Thread.currentThread().getName() + "_syncObjectMethod: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
try {
System.out.println(Thread.currentThread().getName() + "_syncObjectMethod_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncObjectMethod_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread A_thread = new Thread(syncThread, "A_thread");
Thread B_thread = new Thread(syncThread, "B_thread");
A_thread.start();
B_thread.start();
// A_thread_syncThisBlock: : 17:04:40
// B_thread_syncObjectMethod: : 17:04:40
// B_thread_syncObjectMethod_Start: 17:04:41
// B_thread_syncObjectMethod_End: 17:04:42
// A_thread_syncThisBlock_Start: 17:04:42
// A_thread_syncThisBlock_End: 17:04:43
}
}
如上所示,synchronized 分别用来修饰 this 和实例方法,即两方法锁住的是同一个 syncThread 对象
因此当一个线程访问对象的同步代码块时,另外访问该对象同步方法的线程将会被阻塞,反之亦然
synchronized(类.class) (类锁) 与 synchronized static method() (类锁) 进行比较
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
syncClassBlock();
} else if (threadName.startsWith("B")) {
syncClassMethod();
}
}
private void syncClassBlock() {
System.out.println(Thread.currentThread().getName() + "_syncClassBlock: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_syncClassBlock_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncClassBlock_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
private synchronized static void syncClassMethod() {
System.out.println(Thread.currentThread().getName() + "_syncClassMethod: : "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
try {
System.out.println(Thread.currentThread().getName() + "_syncClassMethod_Start: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "_syncClassMethod_End: "
+ LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread A_thread = new Thread(syncThread, "A_thread");
Thread B_thread = new Thread(syncThread, "B_thread");
A_thread.start();
B_thread.start();
// A_thread_syncClassBlock: : 17:11:04
// B_thread_syncClassMethod: : 17:11:04
// B_thread_syncClassMethod_Start: 17:11:04
// B_thread_syncClassMethod_End: 17:11:05
// A_thread_syncClassBlock_Start: 17:11:05
// A_thread_syncClassBlock_End: 17:11:06
}
}
如上所示,synchronized 分别用来修饰 类.class 和静态方法,即两方法锁住的是同一个 Class 对象
因此当一个线程访问对象的同步代码块时,另外访问该对象同步方法的线程将会被阻塞,反之亦然
当一个线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块
若锁住的是同一个对象,则当一个线程访问对象的同步方法时,另外访问该对象同步方法的线程将会被阻塞
若锁住的是同一个对象,则当一个线程访问对象的同步代码块时,另外访问该对象同步代码块的线程将会被阻塞
若锁住的是同一个对象,则当一个线程访问对象的同步代码块时,另外访问该对象同步方法的线程将会被阻塞,反之亦然
同一个类的不同对象的对象锁互不干扰
由于类锁也是一种特殊的对象锁,因此表现和上述 1、2、3、4 点一致;而由于一个类只会有一把对象锁 (类锁),所以同一个类的不同对象使用类锁将会是同步的
类锁和对象锁互不干扰