synchronized关键字可以保证方法或代码块运行时,同一时刻只有一个方法进入临界区,同时能够保证共享变量在内存的可见性。bash
synchronized能够实现同步代码块、同步方法。数据结构
同步代码块:是经过monitorenter和monitorexit指令,配合monitor实现的。异步
monitor:async
monitor能够理解为一个同步工具或一种同步机制,一般被描述为一个对象。每个Java对象就有一把看不见的锁,称为内部锁或者Monitor锁。Monitor是线程私有的数据结构,每个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每个被锁住的对象都会和一个monitor关联,同时monitor中有一个Owner字段存放拥有该锁的线程的惟一标识,表示该锁被这个线程占用。ide
monitorenter指令插入到同步代码块的开始位置,monitorexit指令插入到同步代码块的结束位置,两条指令一一对应。且任何对象都有一个monitor与之相关联,当且一个monitor被持有以后,他将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor全部权,即尝试获取对象的锁; 工具
同步方法:Class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1。post
Java中的锁根据锁的内容分为:对象锁、类锁。测试
对象锁:在 Java 中,每一个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,一般会被称为“内置锁”或“对象锁”。类的对象能够有多个,因此每一个对象有其独立的对象锁,互不干扰。this
类锁:在 Java 中,针对每一个类也有一个锁,能够称为“类锁”,类锁其实是经过对象锁实现的,即类的 Class 对象锁。每一个类只有一个 Class 对象,因此每一个类只有一个类锁。spa
可用从两个维度进行分类:
修饰代码块:
synchronized(this|object) {}
synchronized(类.class) {}
修饰方法:
修饰非静态方法
修饰静态方法
获取对象锁:
synchronized(this|object) {}
修饰非静态方法
获取类锁:
synchronized(类.class) {}
修饰静态方法
/* 同步线程类 */
class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
sync1();
} else if (threadName.startsWith("C")) {
sync2();
}
}
/**
* 非同步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(this|object) {} 同步代码块
*/
private void sync1() {
System.out.println(Thread.currentThread().getName() + "_Sync1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_Sync1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修饰非静态方法
*/
private synchronized void sync2() {
System.out.println(Thread.currentThread().getName() + "_Sync2: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_Sync2_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync2_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/* 测试代码: */
public class SyncTest {
public static void main(String... args) {
SyncThread syncThread = new SyncThread();
Thread A_thread1 = new Thread(syncThread, "A_thread1");
Thread A_thread2 = new Thread(syncThread, "A_thread2");
Thread B_thread1 = new Thread(syncThread, "B_thread1");
Thread B_thread2 = new Thread(syncThread, "B_thread2");
Thread C_thread1 = new Thread(syncThread, "C_thread1");
Thread C_thread2 = new Thread(syncThread, "C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
/* 运行结果 */
B_thread2_Sync1: 14:44:20
A_thread1_Async_Start: 14:44:20
B_thread1_Sync1: 14:44:20
C_thread1_Sync2: 14:44:20
A_thread2_Async_Start: 14:44:20
C_thread1_Sync2_Start: 14:44:20
A_thread1_Async_End: 14:44:22
A_thread2_Async_End: 14:44:22
C_thread1_Sync2_End: 14:44:22
B_thread1_Sync1_Start: 14:44:22
B_thread1_Sync1_End: 14:44:24
B_thread2_Sync1_Start: 14:44:24
B_thread2_Sync1_End: 14:44:26
C_thread2_Sync2: 14:44:26
C_thread2_Sync2_Start: 14:44:26
C_thread2_Sync2_End: 14:44:28复制代码
结果分析:
A类线程非同步,线程运行过程当中另外的线程也可能访问该对象的非同步代码块,也就是打印结果上显示是start到end中间会被打断。
B类线程中有同步代码块,当一个线程运行过程当中,另外的线程访问同步代码块会被阻塞。注意:synchronized(this|object) {} 代码块 {} 以外的代码依然是非同步的。
C 类线程访问的是 synchronized 修饰非静态方法,C 类线程是同步的,一个线程在访问对象的同步代方法,另外一个访问对象同步方法的线程会被阻塞。synchronized 修饰非静态方法,做用范围是整个方法,因此方法中全部的代码都是同步的。
不难发现,B类线程和C类线程都是访问的同一个对象的对象锁,因此B和C线程间也是同步的。
/* 测试代码: */
public class SyncTest {
public static void main(String... args) {
Thread A_thread1 = new Thread(new SyncThread(), "A_thread1");
Thread A_thread2 = new Thread(new SyncThread(), "A_thread2");
Thread B_thread1 = new Thread(new SyncThread(), "B_thread1");
Thread B_thread2 = new Thread(new SyncThread(), "B_thread2");
Thread C_thread1 = new Thread(new SyncThread(), "C_thread1");
Thread C_thread2 = new Thread(new SyncThread(), "C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
/* 运行结果 */
A_thread2_Async_Start: 15:01:34
C_thread2_Sync2: 15:01:34
B_thread2_Sync1: 15:01:34
C_thread1_Sync2: 15:01:34
B_thread2_Sync1_Start: 15:01:34
B_thread1_Sync1: 15:01:34
C_thread1_Sync2_Start: 15:01:34
A_thread1_Async_Start: 15:01:34
C_thread2_Sync2_Start: 15:01:34
B_thread1_Sync1_Start: 15:01:34
C_thread1_Sync2_End: 15:01:36
A_thread1_Async_End: 15:01:36
C_thread2_Sync2_End: 15:01:36
B_thread2_Sync1_End: 15:01:36
B_thread1_Sync1_End: 15:01:36
A_thread2_Async_End: 15:01:36复制代码
结果分析:两个线程访问不一样对象的 synchronized(this|object) {} 代码块和 synchronized 修饰非静态方法是异步的,同一个类的不一样对象的对象锁互不干扰。
/* 同步线程类 */
class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
sync1();
} else if (threadName.startsWith("C")) {
sync2();
}
}
/**
* 异步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(类.class) {} 同步代码块
*/
private void sync1() {
System.out.println(Thread.currentThread().getName() + "_Sync1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_Sync1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修饰静态方法
*/
private synchronized static void sync2() {
System.out.println(Thread.currentThread().getName() + "_Sync2: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_Sync2_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync2_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/* 测试代码 */
public class SyncTest {
public static void main(String... args) {
SyncThread syncThread = new SyncThread();
Thread A_thread1 = new Thread(syncThread, "A_thread1");
Thread A_thread2 = new Thread(syncThread, "A_thread2");
Thread B_thread1 = new Thread(syncThread, "B_thread1");
Thread B_thread2 = new Thread(syncThread, "B_thread2");
Thread C_thread1 = new Thread(syncThread, "C_thread1");
Thread C_thread2 = new Thread(syncThread, "C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
/* 运行结果 */
B_thread1_Sync1: 15:08:13
C_thread1_Sync2: 15:08:13
B_thread2_Sync1: 15:08:13
A_thread1_Async_Start: 15:08:13
C_thread1_Sync2_Start: 15:08:13
A_thread2_Async_Start: 15:08:13
C_thread1_Sync2_End: 15:08:15
A_thread2_Async_End: 15:08:15
A_thread1_Async_End: 15:08:15
B_thread2_Sync1_Start: 15:08:15
B_thread2_Sync1_End: 15:08:17
B_thread1_Sync1_Start: 15:08:17
B_thread1_Sync1_End: 15:08:19
C_thread2_Sync2: 15:08:19
C_thread2_Sync2_Start: 15:08:19
C_thread2_Sync2_End: 15:08:21复制代码
结果分析:同一对象的状况下,类锁和对象锁的行为一致。
/* 测试代码 */
public class SyncTest {
public static void main(String... args) {
Thread A_thread1 = new Thread(new SyncThread(), "A_thread1");
Thread A_thread2 = new Thread(new SyncThread(), "A_thread2");
Thread B_thread1 = new Thread(new SyncThread(), "B_thread1");
Thread B_thread2 = new Thread(new SyncThread(), "B_thread2");
Thread C_thread1 = new Thread(new SyncThread(), "C_thread1");
Thread C_thread2 = new Thread(new SyncThread(), "C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
/* 运行结果 */
A_thread2_Async_Start: 15:17:28
B_thread2_Sync1: 15:17:28
A_thread1_Async_Start: 15:17:28
B_thread1_Sync1: 15:17:28
C_thread1_Sync2: 15:17:28
C_thread1_Sync2_Start: 15:17:28
C_thread1_Sync2_End: 15:17:30
A_thread2_Async_End: 15:17:30
B_thread1_Sync1_Start: 15:17:30
A_thread1_Async_End: 15:17:30
B_thread1_Sync1_End: 15:17:32
B_thread2_Sync1_Start: 15:17:32
B_thread2_Sync1_End: 15:17:34
C_thread2_Sync2: 15:17:34
C_thread2_Sync2_Start: 15:17:34
C_thread2_Sync2_End: 15:17:36复制代码
结果分析:同一个类的不一样对象的类锁是同一个,也就是多个线程访问同一个类的类锁中的代码仍是同步的。
结果:对象锁和类锁是独立的,互不干扰,非同步。
synchronized关键字不能继承。对于父类中的 synchronized 修饰方法,子类在覆盖该方法时,默认状况下不是同步的,必须显示的使用 synchronized 关键字修饰才行。
定义接口方法时不能使用synchronized关键字。
构造方法不能使用synchronized关键字,但可使用synchronized代码块来进行同步。