• 在java中一般并发编程时候(多线程)才会用到锁,锁的作用就是保证数据的一致性,防止操作数据时出现脏数据。而加上锁就可以保证 共享资源 会被单个线程使用,保证了数据的一致性。(即不会出现多个线程操作同一个资源导致数据异常)。不过引入了锁就会导致一个问题,那就是死锁

  • 死锁
    顾明思义,就是因为锁的缘故(共享资源)导致两个或两个以上线程互相等待(阻塞),和死循环类似。 也可以指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

  • 死锁的产生条件
    1、互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    2、请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
    3、不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    4、环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源
    总结一下就是因为有共享资源,所以有竞争,有竞争后有锁,有锁后有死锁(不过非互斥锁没有死锁,也就是乐观锁,不过会一直自旋)

  • 悲观锁
    顾名思义比较悲观,每次操作之前都上锁,不管用没有竞争都有锁;如果持有时间比较长的时间时,对性能上的开销比较大,因为每次都会将资源锁定。比如在java中synchronized关键字,synchronized一般用于引用类型而不是基本类型。

  • 乐观锁
    也就是每次操作之前,都不上锁。然后通过一定检测手段决定是否更新数据,这种方式下,已经没有所谓的锁概念了,每条线程都直接先去执行操作,计算完成后检测是否与其他线程存在共享数据竞争,如果没有则让此操作成功,如果存在共享数据竞争则可能不断地重新执行操作和检测,直到成功为止。

  • 乐观锁的缺点
    观锁只能保证一个共享变量的原子操作。如上例子,自旋过程中只能保证value变量的原子性,这时如果多一个或几个变量,乐观锁将变得力不从心,但互斥锁能轻易解决,不管对象数量多少及对象颗粒度大小。
    长时间自旋可能导致开销大。假如CAS长时间不成功而一直自旋,会给CPU带来很大的开销。
    ABA问题。CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。