synchronized 只看一篇就够了

这个关键字涉及的知识点比较多。这里准备从她的作用。她的使用。她的原理。以及她的兄弟姐妹四个方面来讲。
首先她的作用。
1.多线程同步里头作为 锁 能锁类。能索对象。能锁代码块
2.在synchronized包含的代码块开头跟结尾的部分加入了共享变量的数据同步,我个人认为第二条才是为啥这个关键字起名的原因。为了同步嘛。至于如何同步在使用过程中一一解析

先看常用锁的部分。

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

锁对象跟类 有点太抽象。换成通俗的说法。对象就是每次你new 出来的那个Object。类其实也是一个Object. 比如Integer.class。但是这类class 是单例的。就是有且只有一个。下面用实际代码来加深印象。

第一种 代码块
synchronized(XXX){
}

这里XXX 基本可以是具体的对象。 Object a=new Object(); synchronized(a){} 这种对象一旦被锁上。这个对象其他的被synchronized包含的不管是方法(要抛去static synchronized 锁的是 Object.class对象是另一个对象)还是代码块 是都进不去的。只有等待{}完成。但是如果是Object b=new Object() 是另一个对象。b的所有方法还是可以随便访问的。

如果XXX是 this这个关键字 锁的是具体的对象。还是上面那个规则。同对象就要等代码块执行完。不同对象无所谓。

如果XXX是 BBB.class 。比如
synchronized 只看一篇就够了_第1张图片
那不管是FlyweightFactory a=new FlyweightFactory(); FlyweightFactory b=new FlyweightFactory();
那不管是a.getFlyweightMapSize 还是b.getFlyweightMapSize 都会被锁。这里锁的是一个独一无二的Class对象
这个Class对象跟 this是不同的对象。所以下面的代码块是依然可以进入的。
synchronized 只看一篇就够了_第2张图片
最后看看加在静态方法上会是什么结果。锁的是类还是对象。答案是类。也就是等同于synchonized(class)。
synchronized 只看一篇就够了_第3张图片
这两个方法执行的时候 一旦有一个进入了代码块,另一个就要等代码块执行完。注意是代码块。就是{}。ok到这里使用就说完了。下面开始讲原理 synchonized究竟干了啥

这里我不想跟其他文章一样开始说什么monitor对象 java对象头 这些都是具体细节。我就想讲一个基本的实现逻辑。 这个synchonized东西是跟我们现实场景是很像的.就是去银行办理业务。你进门就要拿一个号。前面要有人你就要排队。实际上不管是她的兄弟Lock还是后面其他各种升级版本 AQS 都是这个逻辑,就是拿一个凭证进队列。没到你,你就只能等。当然为了更有效率。就有了各种锁。早期synchonized还是很慢的。但是随着jdk的升级。底层对synchonized这个做了很多优化。具体不讲了。总结一句话就是有了各种场景的判断,在不同的场景下让效率更高。因为都是底层的活这里就一一说明了。最后要讲一个synchonized 对成员变量可见性的影响 。就是 synchonized代码块里头如果用到了 成员变量或者说是共享变量。那进入的时候会强制刷新一次从共享内存到工作内存的同步。 同理,在退出的时候,会完成一次工作内存到共享内存的同步。这个到底意义何在。

一定要记住是在代码块全部完成的时候,才会同步到共享内存。比如有给boolean成员变量 一个方法是修改。一个方法是读。假如在方法执行到修改的方法执行到一半boolean 从true变成false,但是这个false是工作内存的变量值。 但是方法的因为底层调度切换到了读的方法。对不起这个读的方法因为读的还是主内存的拷贝还是ture。内存可见性问题来了。要解决这个问题咋办呢。第一种是给读写的方法都加上synchonized。有啥作用呢。就是延迟读。大家脑补一下。假如还是刚才执行到一半。但是方法还没结束。线程确实也切换了读。但是发现因为加了锁进不去方法。只能等待刚才的方法完全执行完。而方法执行完的时候工作内存已经完成了同步了。主内存的值已经变成了false。再切换到读方法的时候。因为锁解除。方法能进。第二个变量要完成一次主内存到工作内存同步。已经是false。刚才的不同步的问题就解决了。 第二种就是涉及到了volatile这个关键字。后面再讲。

最后要说说synchonized 的一些缺点。很多场景实现不了的。第一个就是我可以拿锁,但是拿不到我不想等的情况 。第二种是两个方法。对一个共享变量。这个共享变量没有别的地方独写。一读一写的情况。读的时候可以随便进。写的时候要看有没有锁。有写锁或者读锁都不能进。这两种情况synchonized 都没法实现。第三种情况 保证排队的顺序。synchonized的队列默认是非公平的。也就是排队10人。先来的未必先执行,看运气。 synchonized 会导致死锁。下面给一个最简单的死锁里头
Object a ,b

synchonized(a){
synchonized(b){
}
}

synchonized(b){
synchonized(a){
}
}

很简单。就是多个对象嵌套使用的时候。同时进a。同时进b。 a拿不到b。 b也拿不到a。 所以在有嵌套的时候要小心。

你可能感兴趣的:(并发编程)