synchronized
工做原理及使用小结为确保共享变量不会出现并发问题,一般会对修改共享变量的代码块用
synchronized
加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题java
本篇将集中在synchronized
关键字的工组原理以及使用方式上安全
以一个case进行分析,源码以下多线程
public class SynchronizedDemo { public void method() { synchronized (this) { System.out.println("Method 1 start"); } } }
在加锁的代码块, 多了一个 monitorenter
, monitorexit
并发
每一个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的全部权,过程以下:性能
执行monitorexit的线程必须是objectref所对应的monitor的全部者。测试
锁this
谈到
synchronized
就不可避免的要说到锁这个东西,基本上在网上能够搜索到一大批的关于偏向锁,轻量锁,重量锁的讲解文档,对这个东西基本上我也不太理解,多看几篇博文以后,简单的记录一下操作系统
先抛一个结论: 轻量级锁是为了在线程交替执行同步块时提升性能,而偏向锁则是在只有一个线程执行同步块时进一步提升性能线程
获取过程code
释放过程
“轻量级”是相对于使用操做系统互斥量来实现的传统锁而言的。
可是,首先须要强调一点的是,轻量级锁并非用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减小传统的重量级锁使用产生的性能消耗。
在解释轻量级锁的执行过程以前,先明白一点,轻量级锁所适应的场景是线程交替执行同步块的状况,若是存在同一时间访问同一锁的状况,就会致使轻量级锁膨胀为重量级锁
简单来说,单线程时,使用偏向锁,若是这个时候,又来了一个线程访问这个代码块,那么就要升级为轻量锁,若是这个线程在访问代码块同时,又来了一个线程来访问这个代码块,那么就要升级为重量锁了。下面更多的显示了这些变更时,标记位的随之改变
修饰实例方法
多个线程访问同一个实例的加锁方法时,会出现锁的竞争
修饰静态方法
多个线程访问类的加锁方法时,会出现锁的竞争
修饰代码块
多线程访问到同一个代码块时,会出现竞争的问题
一个case: TestDemo方法定义以下
public class TestDemo { public synchronized void a() { // ... } public synchronized void b() { // ... } public static synchronized void c() { // ... } public static synchronized void d() { // ... } public void e() { // ... } public void f() { synchronized(this) { // .... } } public void g() { synchronized(this) { // .... } } }
对上面的问题,核心的一点就是synchronized
是否只做用于修饰的代码块or方法上
TestDemo的具体实现以下
public class TestDemo { public synchronized void a(String msg) { System.out.println(Thread.currentThread().getName() + ":a() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":a() after: " + msg); } public synchronized void b(String msg) { System.out.println(Thread.currentThread().getName() + ":b() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":b() after: " + msg); } public static synchronized void c(String msg) { System.out.println(Thread.currentThread().getName() + ":c() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":c() after: " + msg); } public static synchronized void d(String msg) { System.out.println(Thread.currentThread().getName() + ":d() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":d() after: " + msg); } public void e(String msg) { System.out.println(Thread.currentThread().getName() + ":e() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":e() after: " + msg); } public void f(String msg) { System.out.println(Thread.currentThread().getName() + ":f() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":f() after: " + msg); } public void g(String msg) { synchronized (this) { System.out.println(Thread.currentThread().getName() + ":a() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":a() after: " + msg); } } public void h(String msg) { synchronized (this) { System.out.println(Thread.currentThread().getName() + ":h() before"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":h() after: " + msg); } } }
测试一 实例加锁方法的访问测试
/** * 非静态同步方法测试 */ private void nonStaticSynFun() throws InterruptedException { TestDemo testDemo = new TestDemo(); Thread thread1 = new Thread(()->testDemo.a("访问同一加锁方法"), "thread1"); Thread thread2 = new Thread(()->testDemo.a("访问同一加锁方法"), "thread2"); System.out.println("---两个线程,访问同一个加锁方法开始---"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("---两个线程,访问同一个加锁方法结束---\n"); // TestDemo testDemo2 = new TestDemo(); thread1 = new Thread(()->testDemo.a("访问第一个实例同一加锁方法"), "thread1"); thread2 = new Thread(()->testDemo2.a("访问第二个实例同一加锁方法"), "thread2"); System.out.println("---两个线程,访问两个实例同一个加锁方法开始---"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("---两个线程,访问两个同一个加锁方法结束---\n"); // thread1 = new Thread(()->testDemo.a("访问两个加锁方法"), "thread1"); thread2 = new Thread(()->testDemo.b("访问两个加锁方法"), "thread2"); System.out.println("---两个线程,访问两个加锁方法开始---"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("---两个线程,访问两个加锁方法结束---\n"); // thread1 = new Thread(()->testDemo.a("访问加锁实例方法"), "thread1"); thread2 = new Thread(()->TestDemo.c("访问加锁静态方法"), "thread2"); System.out.println("---两个线程,访问实例和静态加锁方法开始---"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("---两个线程,访问实例和静态加锁方法结束---\n"); } @Test public void testNoStaticSynFun() throws InterruptedException { for(int i = 0; i < 2000; i++) { nonStaticSynFun(); } }
上面的测试case主要覆盖:
输出结果以下
---两个线程,访问同一个加锁方法开始--- thread1:a() before thread1:a() after: 访问同一加锁方法 thread2:a() before thread2:a() after: 访问同一加锁方法 ---两个线程,访问同一个加锁方法结束--- ---两个线程,访问两个实例同一个加锁方法开始--- thread1:a() before thread2:a() before thread2:a() after: 访问第二个实例同一加锁方法 thread1:a() after: 访问第一个实例同一加锁方法 ---两个线程,访问两个同一个加锁方法结束--- ---两个线程,访问两个加锁方法开始--- thread1:a() before thread1:a() after: 访问两个加锁方法 thread2:b() before thread2:b() after: 访问两个加锁方法 ---两个线程,访问两个加锁方法结束--- ---两个线程,访问实例和静态加锁方法开始--- thread1:a() before thread2:c() before thread2:c() after: 访问加锁静态方法 thread1:a() after: 访问加锁实例方法 ---两个线程,访问实例和静态加锁方法结束---
验证结果:
测试case二: 静态加锁方法测试
private void staticSynFun() throws InterruptedException { Thread thread1 = new Thread(() -> TestDemo.c("访问加锁静态方法"), "thread1"); Thread thread2 = new Thread(() -> TestDemo.c("访问加锁静态方法"), "thread2"); System.out.println("---两个线程,访问静态加锁方法开始---"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("---两个线程,访问静态加锁方法结束---\n"); // TestDemo testDemo1 = new TestDemo(), testDemo2 = new TestDemo(); thread1 = new Thread(() -> testDemo1.c("访问加锁静态方法"), "thread1"); thread2 = new Thread(() -> testDemo2.d("访问加锁静态方法"), "thread2"); Thread thread3 = new Thread(() -> testDemo1.a("访问加锁实例方法"), "thread3"); System.out.println("---两个线程,访问不一样实例的静态加锁方法开始---"); thread1.start(); thread2.start(); thread3.start(); thread1.join(); thread2.join(); thread3.join(); System.out.println("---两个线程,访问不一样实例的静态加锁方法结束---\n"); } @Test public void testStaticSynFunc() throws InterruptedException { for (int i = 0; i < 2000; i++) { staticSynFun(); } }
上面的测试主要覆盖
输出结果以下
---两个线程,访问静态加锁方法开始--- thread1:c() before thread1:c() after: 访问加锁静态方法 thread2:c() before thread2:c() after: 访问加锁静态方法 ---两个线程,访问静态加锁方法结束--- ---两个线程,访问不一样实例的静态加锁方法开始--- thread1:c() before thread3:a() before thread1:c() after: 访问加锁静态方法 thread2:d() before thread3:a() after: 访问加锁实例方法 thread2:d() after: 访问加锁静态方法 ---两个线程,访问不一样实例的静态加锁方法结束---
验证结果:
测试case三: 同步代码块
基本上和上面的相同,同步代码块分为静态同步代码块(共享类锁);非静态同步代码块(共享实例锁)
synchronized
三中使用姿式,修饰静态方法,实例方法,(静态/非静态)代码块synchronized
底层主要是经过偏向锁,轻量级锁和重量级锁组合来实现线程同步的功能GG