synchronized关键字详解

synchronized(源于马士兵教育课程)

1.synchronized关键字

public class T {
	private int count = 10;
	private Object o = new Object();
	public void m() {
		synchronized(o) { //任何线程要执行下面的代码,必须先拿到o的锁
			count--;
			System.out.println(Thread.currentThread().getName() + " count = " + count);
		}
	}
}
public class T {
	private int count = 10;
	public void m() {
		synchronized(this) { //任何线程要执行下面的代码,必须先拿到this的锁
			count--;
			System.out.println(Thread.currentThread().getName() + " count = " + count);
		}
	}
}
public class T {
	private int count = 10;
	public synchronized void m() { //等同于在方法的代码执行时要synchronized(this)
		count--;
		System.out.println(Thread.currentThread().getName() + " count = " + count);
	}
}
public class T {
	private static int count = 10;
	public synchronized static void m() { //这里等同于synchronized(FineCoarseLock.class)
		count--;
		System.out.println(Thread.currentThread().getName() + " count = " + count);
	}
	public static void mm() {
		synchronized(T.class) { //考虑一下这里写synchronized(this)是否可以?
			count --;
		}
	}
}
  • synchronized对象锁
  • synchronized类锁

2.同步和非同步方法是否可以同时调用?

package com.mashibing.juc.c_007;
public class T {
	public synchronized void m1() { 
		System.out.println(Thread.currentThread().getName() + " m1 start...");
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " m1 end");
	}
	public void m2() {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " m2 ");
	}
	public static void main(String[] args) {
		T t = new T();
		new Thread(t::m1, "t1").start();
		new Thread(t::m2, "t2").start();
	}
}
  • 输出:
t1 m1 start...
t2 m2 
t1 m1 end
  • 可以同时调用

3.synchronized锁的可重入性

/**
 * 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
 * 也就是说synchronized获得的锁是可重入的
 * @author mashibing
 */
package com.mashibing.juc.c_009;
import java.util.concurrent.TimeUnit;
public class T {
	synchronized void m1() {
		System.out.println("m1 start");
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		m2();
		System.out.println("m1 end");
	}
	synchronized void m2() {
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("m2");
	}
	public static void main(String[] args) {
		new T().m1();
	}
}
/**
 * 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
 * 也就是说synchronized获得的锁是可重入的
 * 这里是继承中有可能发生的情形,子类调用父类的同步方法
 * @author mashibing
 */
package com.mashibing.juc.c_010;
import java.util.concurrent.TimeUnit;
public class T {
	synchronized void m() {
		System.out.println("m start");
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("m end");
	}
	public static void main(String[] args) {
		new TT().m();
	}
}
class TT extends T {
	@Override
	synchronized void m() {
		System.out.println("child m start");
		super.m();
		System.out.println("child m end");
	}
}
  • 若synchronized不可重入,则上述代码会发生死锁

4.synchronized与异常

/**
 * 程序在执行过程中,如果出现异常,默认情况锁会被释放
 * 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。
 * 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,
 * 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。
 * 因此要非常小心的处理同步业务逻辑中的异常
 * @author mashibing
 */
package com.mashibing.juc.c_011;
import java.util.concurrent.TimeUnit;
public class T {
	int count = 0;
	synchronized void m() {
		System.out.println(Thread.currentThread().getName() + " start");
		while(true) {
			count ++;
			System.out.println(Thread.currentThread().getName() + " count = " + count);
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(count == 5) {
				int i = 1/0; //此处抛出异常,锁将被释放,要想不被释放,可以在这里进行catch,然后让循环继续
				System.out.println(i);
			}
		}
	}
	public static void main(String[] args) {
		T t = new T();
		Runnable r = new Runnable() {
			@Override
			public void run() {
				t.m();
			}
		};
		new Thread(r, "t1").start();
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new Thread(r, "t2").start();
	}
}
  • 出现异常,默认情况锁会被释放

5.synchronized锁住的对象不可是string,Integer,Long等常量对象

6.synchronized的底层实现

synchronized的底层实现
JDK早期的 重量级 - OS
后来的改进
锁升级的概念:
sync (Object)
markword 记录这个线程ID (偏向锁)
如果线程争用:升级为 自旋锁
10次以后,
升级为重量级锁 - OS
偏向锁->自旋锁->重量级锁
前两者占用CPU,重量级锁进入等待队列,不占用CPU
执行时间短(加锁代码),线程数少,用自旋
执行时间长,线程数多,用系统锁

原文地址:https://www.cnblogs.com/fjf3997/p/13217556.html