synchronized关键字能够保证方法或代码块运行时,同一时刻只有一个方法进入临界区,同时可以保证共享变量在内存的可见性。
synchronized可以实现同步代码块、同步方法。
同步代码块:是通过monitorenter和monitorexit指令,配合monitor实现的。
monitor:
monitor可以理解为一个同步工具或一种同步机制,通常被描述为一个对象。每一个Java对象就有一把看不见的锁,称为内部锁或者Monitor锁。Monitor是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联,同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。
monitorenter指令插入到同步代码块的开始位置,monitorexit指令插入到同步代码块的结束位置,两条指令一一对应。且任何对象都有一个monitor与之相关联,当且一个monitor被持有之后,他将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获取对象的锁;
同步方法:Class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1。
Java中的锁根据锁的内容分为:对象锁、类锁。
对象锁: 在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
类锁: 在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。
可用从两个维度进行分类:
修饰代码块:
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代码块来进行同步。
blog.csdn.net/chenssy/art…
juejin.im/post/594a24…