关于synchronized不可描述的事情

    科技2022-07-11  106

    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去重新唤醒进入竞争队列,然后去竞争锁。性能较低。好比我问了几次,都没人有钱,我觉得搬个椅子坐着等谁有钱。
    Processed: 0.037, SQL: 8