1.synchronized的实现方式和影响效果
public static synchronized void demo1() {
System.out.println("静态方法锁 : demo1");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void demo3() {
System.out.println("实例方法锁 : demo3");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
-
代码块锁
使用这样的锁,根据锁的对象,可以实现上述的实例方法锁和静态方法锁,并且比之更加灵活性和效率。因为在锁外的代码是可以定制的,且不需要竞争锁。好比一个人拿了镰刀割草,但是他说这句话之前是否先跳个舞还是三百六十度转圈再割草,这个别人管不着。
//1. 等同于 静态方法锁 private static Object lock = new Object();//因为这个是共享变量,所以锁住的代码也是全局锁住的 public void demo2() { synchronized (lock){//或者lock 换成 this.getClass() System.out.println("全局代码块锁 : demo2"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } //2.等同上面的实例方法锁 且灵活性更高 public void demo4() { synchronized (this){ System.out.println("实例代码块锁 : demo4"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } // 3. 改良后的动态范围锁 //指定锁 根据构造方法将锁传入,从而控制是实例锁还是静态锁 private Object lock2; public SynchronizedDemo(Object lock2) { this.lock2 = lock2; } public void demo5() { if (null == lock2) lock2 = this.getClass(); synchronized (lock2){ System.out.println("范围可控锁 : demo5 : " + lock2.toString()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
main方法,看测试效果
public static void main(String[] args) throws InterruptedException {
//静态方法锁
new Thread(()->SynchronizedDemo.demo1()).start();
new Thread(()->SynchronizedDemo.demo1()).start();
SynchronizedDemo s1 = new SynchronizedDemo(new Object());
SynchronizedDemo s2 = new SynchronizedDemo(new Object());
new Thread(()->s1.demo2()).start();
new Thread(()->s2.demo2()).start();
new Thread(()->s1.demo3()).start();
new Thread(()->s2.demo3()).start();
new Thread(()->s1.demo4()).start();
new Thread(()->s2.demo4()).start();
new Thread(()->s1.demo5()).start();
new Thread(()->s2.demo5()).start();
Object test = new Object();
SynchronizedDemo s3 = new SynchronizedDemo(test);
SynchronizedDemo s4 = new SynchronizedDemo(test);
System.out.println("test:" + test.toString());
new Thread(()->s3.demo5()).start();
new Thread(()->s4.demo5()).start();
TimeUnit.SECONDS.sleep(100);
}
静态方法锁 : demo1
全局代码块锁 : demo2
实例方法锁 : demo3
实例方法锁 : demo3
范围可控锁 : demo5 : java.lang.Object@33022318
test:java.lang.Object@5fd0d5ae
范围可控锁 : demo5 : java.lang.Object@263e1443
范围可控锁 : demo5 : java.lang.Object@5fd0d5ae
//下面部分在短暂休眠释放锁才会出来==
静态方法锁 : demo1
全局代码块锁 : demo2
实例代码块锁 : demo4
实例代码块锁 : demo4
范围可控锁 : demo5 : java.lang.Object@5fd0d5ae
synchronized锁的底层实现
任何非空对象都可以作为锁,而且根据对象的存放位置,从而影响到锁的范围,静态锁还是实例锁。锁的原理实际上利用对象头的储存空间去实现的,在对象头上标记锁的类型,记录当前获取锁的线程等属性去判断。
膨胀方向:
偏向锁 -> 轻量级锁 -> 重量级锁
- 偏向锁:当两个线程中,基本都是一个线程(比如A)去竞争锁,那么记录下A线程,然后A线程去竞争锁时直接给予权限。(实际应用中很少有这种情况,甚至我们会去jvm设置禁用偏向锁)。这个就好比你和别人家的孩子玩耍的时候,打碎东西,挨揍的总是你一样。
-XX:-UseBiasedLocking //关闭偏向锁(默认打开)
- 轻量级锁:最主要的锁方式,当线程竞争锁失败之后,会通过自旋的方式,再去竞争锁,如果获取成功,就继续执行,失败则自旋,直到自旋次数达到指定阈值(1.6后好像会自适应出阈值,以前默认是20次),则膨胀为重量级锁。好比我问问有没有人能给我5块钱,没有啊,那我过会再问。
JDK1.6 -XX:+UseSpinning;//开启
-XX:PreBlockSpin=10;// 为自旋次数;
JDK1.7后,去掉此自旋次数参数,由jvm控制;
- 重量级锁:这是一个阻塞锁,如果线程竞争锁失败,就会被放入Blocking-queue的阻塞队列,等待锁被释放,根据notify或notifyall去重新唤醒进入竞争队列,然后去竞争锁。性能较低。好比我问了几次,都没人有钱,我觉得搬个椅子坐着等谁有钱。