1.作用及版本
- 多线程环境下用来控制资源同步访问的,同步控制的代码块是原子操作。
-
JDK1.6以前的synchronized是一把重量级锁,监视器锁(monitor),当一个线程获取到锁之后,其他线程想要获取锁就必须等待,也就是其他线程是阻塞状态(Blocked)。当前线程获取到锁执行完之后释放锁,这时阻塞队列里面的线程会去竞争锁,竞争到锁的线程又继续执行。由于这个时候发生了线程切换,因此操作系统由用户态变成核心态,这个过程是比较耗时的,因此在JDK1.6以后对synchronized关键字进行了优化。
2.作用域
- 方法
-
静态方法:相当于类锁,所有对象共享类锁。
-
package com.sap.leo; public class Concurrency extends Thread{ private static int data = 0; public Concurrency(String name) { super(name); } public static void main(String[] args) throws InterruptedException { Concurrency c1 = new Concurrency("c1"); Concurrency c2 = new Concurrency("c2"); Concurrency c3 = new Concurrency("c3"); Concurrency c4 = new Concurrency("c4"); c1.start(); c2.start(); c3.start(); c4.start(); Thread.sleep(60*2*1000); } @Override public void run() { synchronized (Concurrency.class) { try { if(Thread.currentThread().getName().equals("c1")) { Thread.currentThread().sleep(60000); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
-
非静态方法:对象锁;
-
package com.sap.leo; public class Concurrency extends Thread{ public static void main(String[] args) throws InterruptedException { Concurrency c = new Concurrency(); Thread t1 = new Thread(c,"t1"); Thread t2 = new Thread(c,"t2"); Thread t3 = new Thread(c,"t3"); Thread t4 = new Thread(c,"t4"); t1.start(); t2.start(); t3.start(); t4.start(); Thread.sleep(10000); } @Override public synchronized void run() { if("t1".equals(Thread.currentThread().getName())) { System.out.println("t1 get lock..."); System.out.println("t1 sleep 5s..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1 release lock..."); } else { System.out.println(Thread.currentThread().getName()); } } }
-
- 代码块
3.字节码层面分析
package com.sap.leo;
public class Concurrency {
private int data = 0;
public static void main(String[] args) throws InterruptedException {
Concurrency c = new Concurrency();
for(int i=0;i<1000;i++)
{
new Thread(()->{
c.manipulateData();
}).start();
}
Thread.sleep(5000);
System.out.println(c.data);
}
public int manipulateData()
{
synchronized (this)
{
data++;
}
return this.data;
}
}
- synchronized语句同步代码块是使用的monitorenter和monitorexit指令,monitorenter表示进入同步代码块,当线程获取到锁之后,锁的计数器就加1,当执行到monitorexit的时候,锁的计数器减1。synchronized的对象锁是可以重入的,没重入一次对象锁的计数器就加1.
- 下图有两个monitorexit,第一个表示锁释放之后正常退出,第二个monitorexit表示异常情况的退出。
4.synchronized滥用:死锁
-
package com.sap.leo.concurrency; public class DeadLock { private static String resource1 = "r1"; private static String resource2 = "r2"; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (resource1) { System.out.println("Thread "+ Thread.currentThread().getName() + " got resource1..." ); long startTime = System.currentTimeMillis(); System.out.println("Wait for getting resource2... "); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (resource2) { System.out.println("Thread "+ Thread.currentThread().getName() + " got resource2..." ); } } } }, "t1"); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { Thread.currentThread().yield(); synchronized (resource2) { System.out.println("Thread "+ Thread.currentThread().getName() + " got resource2..." ); try { System.out.println("Wait for getting resource1... "); Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resource1) { System.out.println("Thread "+ Thread.currentThread().getName() + " got resource1..." ); } } } }, "t2"); t2.start(); } }
5.synchronized锁优化技术(JDK1.6以后)
- 偏向锁:在多线程不存在竞争的情况下,第一次拿到锁的线程在后面更有机会再次获取到锁。如果有锁竞争,那么就会膨胀成轻量级锁。采用CAS算法。偏向锁在Mark Word(32位)中锁标志位(2字节)为01,1位用来专门标志它是标志位。
- 轻量级锁:当多线程竞争执行时,线程A尝试使用CAS去修改Mark Word,修改成功,锁标志位设置为00;线程B修改Mark Word失败则自旋,自旋还是失败的话,锁会膨胀为重量级锁。
- 重量级锁:线程A获取到锁,其他线程都得阻塞,不需要自旋,整个处理时间比较缓慢。