- synchronized关键字和Lock的实现类都是悲观锁
- 认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改
- 适合写操作多的场景,先加锁可以保证写操作时数据正确(写操作包括增删改)、显式的锁定之后再操作同步资源
- 乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作
- 乐观锁在Java中通过使用无锁编程来实现,最常采用的时CAS算法,Java原子类中的递增操作就通过CAS自旋实现的
- 适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅度提升
- 乐观锁一般有两种实现方式(采用版本号机制、CAS算法实现)
公平锁:是指多个线程按照申请锁的顺序来获取资源类
非公平锁:是指在多线程获取锁的顺序并不是按照申请锁的顺序,在高并发的情况下,有可能造成优先级反转或者锁饥饿现象
- ReentrantLock默认是非公平锁。传入参数true,表示声明一个公平锁对象。
- 非公平锁能更充分的利用CPU的时间片,尽量减少CPU空闲状态时间
- 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当一个线程请求锁获取同步状态,然后释放同步状态,因为不需要考虑是否还有前驱节点,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大了,所以就减少了线程的开销线程的开销
- 如果为了更高的吞吐量,非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了。否则那就用公平锁。
- 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没有释放而阻塞。
- Java中ReentrantLock和Synchronized都是可重入锁,可重入锁的一个优点是可在一定程度避免死锁。
- 隐式锁(synchronized)默认是可重入锁,在同步块、同步方法使用。
- 显示锁(ReentrantLock)lock和unlock一定要一 一匹配,如果少了或多了,都会影响别的线程。
- 每个锁对象在ObjectMonitor中拥有一个锁计数器(_count)和一个指向持有该锁的线程的指针(_owner)
- 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将计数器加1
- 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程时当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直到持有线程释放该锁
- 当执行monitorexit,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已经释放
- 死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去。
- 系统资源不足
- 进程运行推进的顺序不合适
- 资源分配不当
-
命令行
- jps 查看正在运行的Java进程
- jstack + 进程号
- 如果发现死锁会显示 Found one Java-level deadlock: 以及详细信息。
-
Arthas
- 使用thread -b命令 几个查看是否有死锁以及详细信息



