对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,可是这synchronized并不是性能最优的。今天我就简单介绍一下几种锁。可能我下面讲的时候其实不少东西不会特别深入,最好的方式是本身作实验,把各类场景在代码中实验一下,这样发发现不少细节。java
做为Java中的轻量级锁,当多线程中一个线程操做后能够保证其余线程可见,也就是书上所说的“可见性”,另一个就是“重排序”。所谓重排序指的是JVM对指令的优化。不少人可能在实际实验中发现好像不是如此,最后的例子我也会说明这一点。安全
这个做为Java中“重量级”的线程安全机制被你们所熟知,这个就不在这里作解释了。多线程
好,说一下ReentrantLock,这个锁主要是能显示的添加锁和释放锁,好处是更加灵活,可以更加准确的控制锁,也能确保系统的稳定,好比说“重连”。后面代码会有使用到。 并发
上面介绍完了几种锁,下面用具体的代码来看看几种锁的实际用法,以及各类表现形式。代码有点长,这里最好本身实验一下,而后看看结果,并思考这个结果。性能
输出结果:优化
i>>>>>381890url
vi>>>>>353610线程
ai>>>>>400000排序
si>>>>>392718ip
ri>>>>>392658
从上面的输出结果来看真是让人大感意外:只有原子操做AtomicInteger的结果保证了多线程的安全性,而其余无论是用轻量级的volatile仍是重量级的synchronized都没有达到咱们想要的效果。这也让我产生了大在的怀疑。难道问题真的这么蹊跷?
从这里不难看出除了AtomicInteger用的是其本身的方法而其余都是用到了Java的语法糖++操做。而这让我想起了++操做并不是原子操做,而可能在其中间操做致使了其余线程对其余进行了修改,虽然一样的问题我在《Think in Java》中也找到能够佐证的例子。这里有一个问题就是synchronized:由于我对si已经加了synchronized操做,可是输出的结果使人意外,难道还会有问题?这让我想把这段代码编译成字节码的冲动。好吧,下面看字节码(这里我单独把synchronized这一段操做抽出来,做为分析,其余几个就算了,否则编译后的字节码有点多)
为了方便看,先贴出源代码
下面是字节码,为了节省篇幅,一些不重要的部分我将不贴出
从这里一看从monitorenter进入安全区到monitorexit出安全区没有发现si是处于中间状态的,那又是在哪出的问题呢?这里简单说一下,归根结底仍然是(++)操做非原子操做,但是不少人疑惑了,这里不是加锁了吗?废话很少说,在个人深刻探析Java线程锁机制有一个比较详细的分析。