JUC并发编程(五)

因为内容太多了,所以将其拆分为以下内容

参考

https://www.bilibili.com/video/BV1B7411L7tE

彻底玩转单例模式

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 饿汉式单例模式
*/
public class Hungry {

// 浪费空间
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private byte[] data3=new byte[1024*1024];
private byte[] data4=new byte[1024*1024];

private Hungry(){

}

private final static Hungry HUNGRY=new Hungry();

private static Hungry getInstance(){
return HUNGRY;
}

public static void main(String[] args) {

}
}

DCL懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 懒汉式单例模式
* 道高一尺魔高一丈
*/
public class LazyMan {

private static boolean flag = false;

private LazyMan() {
synchronized (LazyMan.class) {
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("不要试图利用反射破坏异常");
}
}
}

private volatile static LazyMan lazyMan;

public static LazyMan getInstance() {
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
/**
* 不是原子性操作
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
}
}
}

return lazyMan;
}


public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 多线程并发
// for (int i = 0; i < 10; i++) {
// new Thread(() -> {
// LazyMan.getInstance();
// }).start();
// }


// 反射
// LazyMan instance1=LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
LazyMan instance3 = declaredConstructor.newInstance();

// System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
}
}

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 静态内部类
*/
public class Holder {

private Holder() {

}

public static Holder getInstance() {
return InnerClass.HOLDER;
}

public static class InnerClass {

private final static Holder HOLDER = new Holder();
}

}

单例不安全,反射

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public enum EnumSingle {
INSTANCE;

public EnumSingle getInstance() {
return INSTANCE;
}
}

class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);

EnumSingle instance2=declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}

枚举的最终反编译,源码有的是有参构造

深入理解CAS

什么是 CAS

大厂必须要研究底层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CASDemo {

// CAS compareAndSet : 比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger=new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果期望值达到了,就更新,否则不更新 ,CAS 是 CPU 并发的原语!
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());

System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}

Unsafe 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}

// Java无法操作内存
// Java可以调用C++ native
// C++可以操作内存
// Java的后门。可以通过这个类操

private volatile int value;

...
}
1
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
1
2
3
4
5
6
7
8
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}

CAS 比较并交换,比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!

缺点:

  1. 循环会耗时

  2. 一次性只能保证一个共享交量的原子性

  3. ABA问题

CAS : ABA 问题(狸猫换太子)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CASDemo {

// CAS compareAndSet : 比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果期望值达到了,就更新,否则不更新 ,CAS 是 CPU 并发的原语!
// ===== 捣乱的线程 ======
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());


System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());

// ===== 期望的线程 ======
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicjavaInteger.get());
}
}

原子引用

解决 ABA 问题,引入原子引用!对应思想:乐观锁!

带版本号的 原子操作!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class CASDemo {

// CAS compareAndSet : 比较并交换
public static void main(String[] args) {
// AtomicInteger atomicInteger = new AtomicInteger(1);

AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1,1);

new Thread(()->{
int stamp = atomicInteger.getStamp(); // 获得版本号
System.out.println("A1=> "+stamp);

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));

System.out.println("A2=> "+atomicInteger.getStamp());

System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));

System.out.println("A3=> "+atomicInteger.getStamp());

},"A").start();
new Thread(()->{
int stamp = atomicInteger.getStamp(); // 获得版本号
System.out.println("B1=> "+stamp);

try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(1, 6, stamp, stamp + 1));
java
System.out.println("B2=> "+atomicInteger.getStamp());

},"B").start();
}
}

注意:

【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 说明:对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在 阿里巴巴 Java 开发手册 ——禁止用于商业用途,违者必究—— 7 /35 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行 判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。 ---《阿里巴巴Java开发手册》

各种锁的理解

公平锁、非公平锁

公平锁:非常公平,不能够插队,必须先来后到! 非公平锁:非常不公平,可以插队(默认都是非公平)

1
2
3
4
5
6
7
public ReentrantLock() {
sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

可重用锁

可重用锁(递归锁)

Synchronized

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Demo1 {
public static void main(String[] args) {
Phone1 phone = new Phone1();

new Thread(() -> {
phone.sms();
},"A").start();

new Thread(() -> {
phone.sms();
},"B").start();
}
}

class Phone1 {
public synchronized void sms() {
System.out.println(Thread.currentThread().getName() + " sms");
call();
}

public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " call");
}
}

Lock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Demo2 {
public static void main(String[] args) {
Phone2 phone = new Phone2();

new Thread(() -> {
phone.sms();
}, "A").start();

new Thread(() -> {
phone.sms();
}, "B").start();
}
}

class Phone2 {

Lock lock = new ReentrantLock();


public synchronized void sms() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " sms");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public synchronized void call() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

自旋锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 自旋锁
*/
public class SpinLockDemo {

AtomicReference<Thread> atomicReference=new AtomicReference<>();

// 加锁
public void myLock(){
Thread thread=Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==> mylock");
while (!atomicReference.compareAndSet(null,thread)){

}
}

// 解锁
public void myUnLock(){
Thread thread=Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==> myUnlock");

atomicReference.compareAndSet(thread,null);
}

}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class TestSpinLock {
public static void main(String[] args) {
SpinLockDemo lock = new SpinLockDemo();

new Thread(() -> {
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {

} finally {
lock.myUnLock();
}

}, "T1").start();

new Thread(() -> {
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {

} finally {
lock.myUnLock();
}
}, "T2").start();

}
}

死锁

死锁是什么

死锁测试,怎么排除死锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DeadLockDemo {

public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}

}
class MyThread implements Runnable{

private String lockA;
private String lockB;
public MyThread(String lockA,String lockB){
this.lockA=lockA;
this.lockB=lockB;
}

@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get"+lockA);
}
}
}
}

解决问题

1、使用 jps 定位进程号

1
2
3
4
5
6
7
C:\Users\18339\Desktop\ideaprojects\juc>jps -l
15792 com.lock.DeadLockDemo
9504 org.jetbrains.jps.cmdline.Launcher
14868 sun.tools.jps.Jps
9512

C:\Users\18339\Desktop\ideaprojects\juc>

2、使用 jstack 进程号 找到死锁问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Found one Java-level deadlock:

"T2":
waiting to lock monitor 0x000002e34e2a0a68 (object 0x00000000d6105220, a java.lang.String),
which is held by "T1"
"T1":
waiting to lock monitor 0x000002e34e2a3248 (object 0x00000000d6105258, a java.lang.String),
which is held by "T2"

Java stack information for the threads listed above:

"T2":
at com.lock.MyThread.run(DeadLockDemo.java:38)
- waiting to lock <0x00000000d6105220> (a java.lang.String)
- locked <0x00000000d6105258> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
"T1":
at com.lock.MyThread.run(DeadLockDemo.java:38)
- waiting to lock <0x00000000d6105258> (a java.lang.String)
- locked <0x00000000d6105220> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.


C:\Users\18339\Desktop\ideaprojects\juc>

面试,工作!排查问题

1、日志

2、堆栈

因为内容太多了,所以将其拆分为以下内容