适合写操作多的场景,先加锁可以保证写操作时数据正确
显示的锁定之后再操作同步资源
认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改.
synchronized关键子和Lock的实现类都是悲观锁
乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据.
如果这个数据没有被更新,当前线程将自己修改的数据成功写入,如果数据已经被其他线程更新了,则根据不同的实现方法执行不同的操作
乐观锁在Java中是通过使用无锁编程来实现的,最常用的CAS算法,Java原子类中的递增操作就通过CAS自旋实现的
适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升.
乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸失之我命,再抢
乐观锁一般有两种实现方式
CAS 方式为乐观锁,synchronized 为悲观锁。因此使用 CAS 解决并发问题通常情况下性能更优。
但使用 CAS 方式也会有几个问题:
ABA问题
因为CAS需要在操作值的时候,检查值有没有发生变化,比如没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时则会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A->B->A就会变成1A->2B->3A。
从Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
循环时间长开销大
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令,那么效率会有一定的提升。pause指令有两个作用:第一,它可以延迟流水线执行命令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零;第二,它可以避免在退出循环的时候因内存顺序冲突(Memory Order Violation)而引起CPU流水线被清空(CPU Pipeline Flush),从而提高CPU的执行效率。
只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。
还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量i = 2,j = a,合并一下ij = 2a,然后用CAS来操作ij。
从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone = new Phone();
new Thread(() -> phone.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone.sendSMS(), "b").start();
}
}
class Phone //资源类
{
public synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
}
// 控制台输出
-------sendEmail
-------sendSMS
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
同理情况1
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone = new Phone();
new Thread(() -> phone.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone.hello(), "b").start();
}
}
class Phone //资源类
{
public synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
public void hello() {
System.out.println("-------hello");
}
}
// 控制台输出
-------hello
-------sendEmail
加个普通方法后发现和同步锁无关
普通方法和同步锁方法是没有关系的不发生争抢
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone1 = new Phone();
//资源类2
Phone phone2 = new Phone();
new Thread(() -> phone1.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone2.sendSMS(), "b").start();
}
}
class Phone //资源类
{
public synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
}
// 控制台
-------sendSMS
-------sendEmail
换成两个对象后,不是同一把锁了,情况立刻变化。
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone = new Phone();
new Thread(() -> phone.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone.sendSMS(), "b").start();
}
}
//资源类
class Phone {
public static synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
}
// 控制台
-------sendEmail
-------sendSMS
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone1 = new Phone();
//资源类2
Phone phone2 = new Phone();
new Thread(() -> phone1.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone2.sendSMS(), "b").start();
}
}
//资源类
class Phone {
public static synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
}
// 控制台
-------sendEmail
-------sendSMS
同理情况5
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone = new Phone();
new Thread(() -> phone.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone.sendSMS(), "b").start();
}
}
//资源类
class Phone {
public static synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
}
// 控制台
-------sendSMS
-------sendEmail
public class Lock8Demo {
//一切程序的入口,主线程
public static void main(String[] args) {
//资源类1
Phone phone1 = new Phone();
//资源类2
Phone phone2 = new Phone();
new Thread(() -> phone1.sendEmail(), "a").start();
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone2.sendSMS(), "b").start();
}
}
//资源类
class Phone {
public static synchronized void sendEmail() {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------sendEmail");
}
public synchronized void sendSMS() {
System.out.println("-------sendSMS");
}
}
// 控制台
-------sendSMS
-------sendEmail
同理7
JDK源码(notify方法)说明
8种锁的案例实际体现在三个地方
javap -c ***.class文件反汇编
javap -v ***.class文件反编译
javap -v -verbose ***.class文件反编译输出附加信息(包括行号,本地变量表,反汇编等详细信息)
synchronized同步代码块:
实现使用的是monitorenter和monitorexit指令
每一个synchronized一定是一个monitorenter和两个monitorexit么?
一般来说会是1:2的情况是第一个是程序正常完成第二个是为了保证当出现异常时可以over程序
但是如果你在程序的结尾throw new RuntimeException的情况下,比例将会是1:1但同时字节码指令中会多出一个 athrow
普通同步方法
调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置.如果设置了,执行线程会将现持有monitor然后在执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor
静态同步方法
比普通同步方法多了一个ACC_STATIC标识从而区分是否为静态同步方法
反编译synchronized锁的究竟是什么? -> 什么是monitor?
为什么任何一个对象都可以成为一个锁?
-> 所有的对象都继承了Object
-> 在HotSpot虚拟机中,omitor采用ObjectMonitor实现
-> \hotspot\agent\src\share\classes\sun\jvm\hotspot\runtimeObjectMonitor.java -> \hotspot\src\share\vm\runtime\ObjectMonitor.cpp -> \hotspot\src\share\vm\runtime\ObjectMonitor.hpp
ObjectMonitor() {
_header = NULL;
_count = 0; // 用来记录该线程获取锁的次数
_waiters = 0,
_recursions = 0; // 锁的重入次数
_object = NULL;
_owner = NULL; // 指向持有ObjectMonitor对象的线程
_WaitSet = NULL; // 存放处于wait状态的线程队列
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // 存放处于等待锁block状态的线程队列
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
-> 每一个对象都天生带一个对象监视器
sychronized必须作用于某个对象中,所以java在对象的头文件储存了锁的相关信息.锁升级功能主要依赖于MarkWord中的锁标志位和释放偏向锁标志位,后续笔记锁升级的时候再加深,没有钱了解一下,下图
Hotspot的实现
codeDemo(公平锁)
public class SaleTicketDemo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "a").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "b").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "c").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "d").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "e").start();
}
}
class Ticket {
private int number = 50;
//默认用的是非公平锁
private Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "\t 卖出第: " + (number--) + "\t 还剩下: " + number);
}
} finally {
lock.unlock();
}
}
}
// 控制台
a 卖出第: 50 还剩下: 49
a 卖出第: 49 还剩下: 48
a 卖出第: 48 还剩下: 47
a 卖出第: 47 还剩下: 46
a 卖出第: 46 还剩下: 45
a 卖出第: 45 还剩下: 44
a 卖出第: 44 还剩下: 43
a 卖出第: 43 还剩下: 42
a 卖出第: 42 还剩下: 41
a 卖出第: 41 还剩下: 40
a 卖出第: 40 还剩下: 39
a 卖出第: 39 还剩下: 38
a 卖出第: 38 还剩下: 37
a 卖出第: 37 还剩下: 36
a 卖出第: 36 还剩下: 35
a 卖出第: 35 还剩下: 34
a 卖出第: 34 还剩下: 33
a 卖出第: 33 还剩下: 32
a 卖出第: 32 还剩下: 31
a 卖出第: 31 还剩下: 30
a 卖出第: 30 还剩下: 29
a 卖出第: 29 还剩下: 28
a 卖出第: 28 还剩下: 27
a 卖出第: 27 还剩下: 26
c 卖出第: 26 还剩下: 25
c 卖出第: 25 还剩下: 24
b 卖出第: 24 还剩下: 23
b 卖出第: 23 还剩下: 22
b 卖出第: 22 还剩下: 21
b 卖出第: 21 还剩下: 20
b 卖出第: 20 还剩下: 19
b 卖出第: 19 还剩下: 18
b 卖出第: 18 还剩下: 17
b 卖出第: 17 还剩下: 16
b 卖出第: 16 还剩下: 15
b 卖出第: 15 还剩下: 14
b 卖出第: 14 还剩下: 13
b 卖出第: 13 还剩下: 12
b 卖出第: 12 还剩下: 11
b 卖出第: 11 还剩下: 10
b 卖出第: 10 还剩下: 9
b 卖出第: 9 还剩下: 8
b 卖出第: 8 还剩下: 7
b 卖出第: 7 还剩下: 6
b 卖出第: 6 还剩下: 5
b 卖出第: 5 还剩下: 4
b 卖出第: 4 还剩下: 3
b 卖出第: 3 还剩下: 2
b 卖出第: 2 还剩下: 1
b 卖出第: 1 还剩下: 0
非公平锁容易出现锁饥饿现象,有的线程不一定会抢到锁
codeDemo(公平锁)
public class SaleTicketDemo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "a").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "b").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "c").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "d").start();
new Thread(() -> {
for (int i = 1; i <= 55; i++) ticket.sale();
}, "e").start();
}
}
class Ticket {
private int number = 50;
//默认用的是非公平锁,分配的平均一点,=--》公平一点
private Lock lock = new ReentrantLock(true);
public void sale() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "\t 卖出第: " + (number--) + "\t 还剩下: " + number);
}
} finally {
lock.unlock();
}
}
}
// 控制台
a 卖出第: 50 还剩下: 49
a 卖出第: 49 还剩下: 48
a 卖出第: 48 还剩下: 47
a 卖出第: 47 还剩下: 46
a 卖出第: 46 还剩下: 45
a 卖出第: 45 还剩下: 44
a 卖出第: 44 还剩下: 43
a 卖出第: 43 还剩下: 42
a 卖出第: 42 还剩下: 41
a 卖出第: 41 还剩下: 40
a 卖出第: 40 还剩下: 39
a 卖出第: 39 还剩下: 38
a 卖出第: 38 还剩下: 37
a 卖出第: 37 还剩下: 36
a 卖出第: 36 还剩下: 35
b 卖出第: 35 还剩下: 34
a 卖出第: 34 还剩下: 33
b 卖出第: 33 还剩下: 32
a 卖出第: 32 还剩下: 31
b 卖出第: 31 还剩下: 30
a 卖出第: 30 还剩下: 29
b 卖出第: 29 还剩下: 28
a 卖出第: 28 还剩下: 27
b 卖出第: 27 还剩下: 26
c 卖出第: 26 还剩下: 25
a 卖出第: 25 还剩下: 24
b 卖出第: 24 还剩下: 23
c 卖出第: 23 还剩下: 22
a 卖出第: 22 还剩下: 21
b 卖出第: 21 还剩下: 20
c 卖出第: 20 还剩下: 19
a 卖出第: 19 还剩下: 18
d 卖出第: 18 还剩下: 17
b 卖出第: 17 还剩下: 16
c 卖出第: 16 还剩下: 15
a 卖出第: 15 还剩下: 14
d 卖出第: 14 还剩下: 13
b 卖出第: 13 还剩下: 12
c 卖出第: 12 还剩下: 11
a 卖出第: 11 还剩下: 10
d 卖出第: 10 还剩下: 9
e 卖出第: 9 还剩下: 8
b 卖出第: 8 还剩下: 7
c 卖出第: 7 还剩下: 6
a 卖出第: 6 还剩下: 5
d 卖出第: 5 还剩下: 4
e 卖出第: 4 还剩下: 3
b 卖出第: 3 还剩下: 2
c 卖出第: 2 还剩下: 1
a 卖出第: 1 还剩下: 0
公平锁相对于非公平锁雨露均沾
生活中,排队讲究先来后到视为公平.程序中的公平性也是符合请求锁的绝对时间的,其实就是FIFO(先入先出队列(First Input First Output, FIFO)这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令.),否则视为不公平
源码解读
按序排队公平锁,就是判断同步队列是否还有先驱节点的存在(我前面还有人么?),如果没有先驱节点才能获取锁;先占先得非公平锁,是不管这个事的,只要能抢获到同步状态就可以
为什么会有公平锁 | 非公平锁的设计为什么默认非公平锁?
使用公平锁会有什么问题?
公平锁保证了排队的公平性,等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大,非公平锁霸气的忽略了这个规则,所以就有可能导致排队的长时间在排队,也没有机会获取到锁,这就是传说中的"锁饥饿"
什么时候用公平?什么时候用非公平?
如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了;否则就用公平锁,大家公平使用
code
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {...}
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {...}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {}
.......
可: 可以
重: 再次
入: 进入
进入什么?
进入同步域(即同步代码块/方法或显式锁锁定的代码)
锁: 同步锁
一句话: 一个线程中的多个流程可以获取同一把锁,只有这把同步锁可以再次进入,自己可以获取自己的内部锁
指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫可重入锁
简单的来说就是: 在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
同步代码块
static Object objectLock = new Object();
public static void main(String[] args)
{
new Thread(() -> {
synchronized (objectLock) {// lock
System.out.println("-----外层");
synchronized (objectLock)
{
System.out.println("-----中层");
synchronized (objectLock)
{
System.out.println("-----内层");
}
}
}//unlock
},"t1").start();
}
// 控制台
-----外层
-----中层
-----内层
以上这种递归写法一般来说没有人会这么写东西,只展示语法规定
与可重入锁相反,不可重入锁不可递归调用,递归调用就发生死锁
lock()和unlock() 1:1的情况下.不会有什么问题
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t" + "-----外层");
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t" + "-----内层");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
lock.lock();
try {
System.out.println("------22222");
} finally {
lock.unlock();
}
}, "t2").start();
}
// 控制台
-----外层
-----中层
-----内层
当lock()多于unlock()的情况下如果只有一个线程程序(可能)会正常运行
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t" + "-----外层");
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t" + "-----内层");
} finally {
}
} finally {
lock.unlock();
}
}, "t1").start();
}
// 控制台
t1 -----外层
t1 -----内层
出大问题的情况,当lock()多于unlock()的情况下多个线程: 注意这种情况只是会导致t2一直没有办法获取锁但是不属于死锁的情况
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t" + "-----外层");
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t" + "-----内层");
} finally {
}
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
lock.lock();
try {
System.out.println("------22222");
} finally {
lock.unlock();
}
}, "t2").start();
}
// 控制台
t1 -----外层
t1 -----内层
问题: 程序没有结束,因为t2蚌住了他没有办法获取到锁,因为t1没有释放完全
死锁是指两个或两个以上的线程在执行过程中,引争夺资源而造成的一种相互等待的现象,若无外力干涉那他们都无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能就很低,否则就会因争夺有限的资源而陷入死锁
code ->死锁Demo
public static void main(String[] args) {
Thread a = new Thread(() -> {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "\t" + " 自己持有A锁,期待获得B锁");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "\t 获得B锁成功");
}
}
}, "a");
a.start();
new Thread(() -> {
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "\t" + " 自己持有B锁,期待获得A锁");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "\t 获得A锁成功");
}
}
}, "b").start();
}
// 控制台
a 自己持有A锁,期待获得B锁
b 自己持有B锁,期待获得A锁
......同时程序没有结束
论证是死锁方法一: terminal查询
## jps -> Java的ps
## 查看所有的正在运行的进程
[azang]$ jps -l
21152 com.atguigu.juc.locks.DeadLockDemo
6592
2532 org.jetbrains.idea.maven.server.RemoteMavenServer36
6836 org.jetbrains.jps.cmdline.Launcher
14728 org.jetbrains.idea.maven.server.RemoteMavenServer36
556 sun.tools.jps.Jps
## 打印出对应进程ID的堆栈信息
[azang]$ jstack 21152
2022-04-28 17:48:03
Full thread dump OpenJDK 64-Bit Server VM (17.0.2+8-LTS mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x000001cc44be2f50, length=15, elements={
0x000001cc435de300, 0x000001cc44800080, 0x000001cc44823cc0, 0x000001cc44824670,
0x000001cc44827030, 0x000001cc448279e0, 0x000001cc44828770, 0x000001cc44829210,
0x000001cc44832950, 0x000001cc435cb4b0, 0x000001cc44a0b5c0, 0x000001cc44b63d50,
0x000001cc44c316a0, 0x000001cc44c31ff0, 0x000001cc171e8720
}
"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=47.87s tid=0x000001cc
435de300 nid=0x3af8 waiting on condition [0x000000851e0ff000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList([email protected]/Native M
ethod)
at java.lang.ref.Reference.processPendingReferences([email protected]/Reference.j
ava:253)
at java.lang.ref.Reference$ReferenceHandler.run([email protected]/Reference.java:
215)
"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=47.87s tid=0x000001cc44800080
nid=0x1c80 in Object.wait() [0x000000851e1fe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
- waiting on <0x0000000623c0d5c8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:155)
- locked <0x0000000623c0d5c8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:176)
at java.lang.ref.Finalizer$FinalizerThread.run([email protected]/Finalizer.java:1
72)
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=47.86s tid=0x000001cc4
4823cc0 nid=0x2924 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=15.62ms elapsed=47.86s tid=0x000001cc44
824670 nid=0x6384 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #6 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=47.86s tid=0x000001cc4482
7030 nid=0x5708 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Deflation Thread" #7 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=47.86s tid=0x00
0001cc448279e0 nid=0x5748 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #8 daemon prio=9 os_prio=2 cpu=46.88ms elapsed=47.86s tid=0x000001c
c44828770 nid=0x49dc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"C1 CompilerThread0" #11 daemon prio=9 os_prio=2 cpu=31.25ms elapsed=47.86s tid=0x000001
cc44829210 nid=0x4214 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"Sweeper thread" #12 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=47.86s tid=0x000001cc448
32950 nid=0x5fc4 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Common-Cleaner" #13 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=47.84s tid=0x000001cc435
cb4b0 nid=0x610c in Object.wait() [0x000000851e9fe000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
- waiting on <0x0000000623d2b3f0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:155)
- locked <0x0000000623d2b3f0> (a java.lang.ref.ReferenceQueue$Lock)
at jdk.internal.ref.CleanerImpl.run([email protected]/CleanerImpl.java:140)
at java.lang.Thread.run([email protected]/Thread.java:833)
at jdk.internal.misc.InnocuousThread.run([email protected]/InnocuousThread.java:1
62)
"Monitor Ctrl-Break" #14 daemon prio=5 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001c
c44a0b5c0 nid=0x62a0 runnable [0x000000851ebfe000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.SocketDispatcher.read0([email protected]/Native Method)
at sun.nio.ch.SocketDispatcher.read([email protected]/SocketDispatcher.java:46)
at sun.nio.ch.NioSocketImpl.tryRead([email protected]/NioSocketImpl.java:261)
at sun.nio.ch.NioSocketImpl.implRead([email protected]/NioSocketImpl.java:312)
at sun.nio.ch.NioSocketImpl.read([email protected]/NioSocketImpl.java:350)
at sun.nio.ch.NioSocketImpl$1.read([email protected]/NioSocketImpl.java:803)
at java.net.Socket$SocketInputStream.read([email protected]/Socket.java:966)
at sun.nio.cs.StreamDecoder.readBytes([email protected]/StreamDecoder.java:270)
at sun.nio.cs.StreamDecoder.implRead([email protected]/StreamDecoder.java:313)
at sun.nio.cs.StreamDecoder.read([email protected]/StreamDecoder.java:188)
- locked <0x00000006238abbf8> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read([email protected]/InputStreamReader.java:177)
at java.io.BufferedReader.fill([email protected]/BufferedReader.java:162)
at java.io.BufferedReader.readLine([email protected]/BufferedReader.java:329)
- locked <0x00000006238abbf8> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine([email protected]/BufferedReader.java:396)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:49)
"Notification Thread" #15 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001
cc44b63d50 nid=0x730 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"a" #16 prio=5 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001cc44c316a0 nid=0x34ec wai
ting for monitor entry [0x000000851eeff000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.atguigu.juc.locks.DeadLockDemo.lambda$main$0(DeadLockDemo.java:27)
- waiting to lock <0x000000062384f1f0> (a java.lang.Object)
- locked <0x000000062384f1e0> (a java.lang.Object)
at com.atguigu.juc.locks.DeadLockDemo$$Lambda$14/0x0000000800c01200.run(Unknown
Source)
at java.lang.Thread.run([email protected]/Thread.java:833)
"b" #17 prio=5 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001cc44c31ff0 nid=0x33d0 wai
ting for monitor entry [0x000000851efff000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.atguigu.juc.locks.DeadLockDemo.lambda$main$1(DeadLockDemo.java:44)
- waiting to lock <0x000000062384f1e0> (a java.lang.Object)
- locked <0x000000062384f1f0> (a java.lang.Object)
at com.atguigu.juc.locks.DeadLockDemo$$Lambda$15/0x0000000800c01418.run(Unknown
Source)
at java.lang.Thread.run([email protected]/Thread.java:833)
"DestroyJavaVM" #18 prio=5 os_prio=0 cpu=93.75ms elapsed=47.80s tid=0x000001cc171e8720 n
id=0x37ec waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=2 cpu=0.00ms elapsed=47.87s tid=0x000001cc435da870 nid=0x1ffc runnab
le
"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc1508a850 nid=0x24d0 runn
able
"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc1508b570 nid=0xa28 ru
nnable
"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc1508bd90 nid=0x2dd8 runnab
le
"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc434a6d90 nid=0x59e0 runn
able
"G1 Service" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc434a77b0 nid=0x5254 runna
ble
"VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=47.80s tid=0x000001cc1727e450 nid
=0x33f0 waiting on condition
JNI global refs: 15, weak refs: 0
####################################################################################################################
## 发现死锁
####################################################################################################################
Found one Java-level deadlock:
=============================
"a":
waiting to lock monitor 0x000001cc44823740 (object 0x000000062384f1f0, a java.lang.Obj
ect),
which is held by "b"
"b":
waiting to lock monitor 0x000001cc44823040 (object 0x000000062384f1e0, a java.lang.Obj
ect),
which is held by "a"
####################################################################################################################
## 问题所在
####################################################################################################################
Java stack information for the threads listed above:
===================================================
"a":
at com.atguigu.juc.locks.DeadLockDemo.lambda$main$0(DeadLockDemo.java:27)
- waiting to lock <0x000000062384f1f0> (a java.lang.Object)
- locked <0x000000062384f1e0> (a java.lang.Object)
at com.atguigu.juc.locks.DeadLockDemo$$Lambda$14/0x0000000800c01200.run(Unknown
Source)
at java.lang.Thread.run([email protected]/Thread.java:833)
"b":
at com.atguigu.juc.locks.DeadLockDemo.lambda$main$1(DeadLockDemo.java:44)
- waiting to lock <0x000000062384f1e0> (a java.lang.Object)
- locked <0x000000062384f1f0> (a java.lang.Object)
at com.atguigu.juc.locks.DeadLockDemo$$Lambda$15/0x0000000800c01418.run(Unknown
Source)
at java.lang.Thread.run([email protected]/Thread.java:833)
Found 1 deadlock.
论证是死锁方法二: win+r 或者mac 终端打开jconsole
连接对应想查看的进程
等待一会儿
进入控制台 -> 点击线程 -> 检查死锁
出现死锁
StampedLock(邮戳锁,票据锁)
不可以String同一把锁 -> 严禁这么搞事情
因为String有一个不可变的常量池,下方代码定义的不是两把锁 ->享元对象
String lockA = "A";
String lockB = "A";