JUC并发编程学习笔记(二)
文章目录
16. JMM
什么是JMM:Java内存模型。
关于JMM的一些同步约定:
- 线程解锁前:必须立即将共享内存同步回主存
- 线程加锁前:必须读取主存中的最新值到工作内存中
- 加锁和解锁是同一把锁
线程 工作内存 主内存
8种操作

问题:线程B修改了值,但线程A不能及时可见。
java内存模型JMM理解整理:https://www.cnblogs.com/null-qige/p/9481900.html
17. Volatile
请你说一下对volatile的理解:volatile是Java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
(原子性:不可分割。线程A在执行任务的时候,不能被打扰,也不能被分割。同时成功或失败。) - 禁止指令重排
Volatile保证可见性Demo:
package com.huathy.volatileTest;
import java.util.concurrent.TimeUnit;
/**
* @ClassPath: com.huathy.volatileTest.JMMDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-16 17:00
*/
/**
* 测试2:
* private static int num = 0;
* 程序输出1,但线程任务仍未停止。
* 两个线程A,B
* A线程工作内存读取num=0。B线程修改工作内存num=1,并回写主内存。
* 但线程A不能及时可见主内存中的num值。
*
* 测试1:
* private volatile static int num = 0;
*
*/
public class JMMDemo {
//不加volatile程序会陷入死循环。
// private static int num = 0;
//加上volatile可以保证程序的可见性
private volatile static int num = 0;
public static void main(String[] args) {
new Thread( ()->{
while (num == 0){
}
} ).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
Volatile不保证原子性,Atomic原子类Demo
问题:如何不使用Synchronized和lock锁,来保证原子性。
可以使用原子类Atomic,其底层直接与操作系统相关。
package com.huathy.volatileTest;
/**
* @ClassPath: com.huathy.volatileTest.VDTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-16 17:18
*/
import java.util.concurrent.atomic.AtomicInteger;
/**
* volatile不保证原子性
*/
public class VDTest {
//volatile不保证原子性
// private volatile static int num = 0;
private static AtomicInteger num = new AtomicInteger();
private static void add(){
// num ++; //这不是一个原子性操作
num.getAndIncrement(); //底层是CAS。Unsafe类,这里的+1操作是在内存中修改值。
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread( ()->{
for (int j = 0; j < 1000; j++) {
add();
}
} ).start();
}
while (Thread.activeCount() > 2){
Thread.yield(); //让出CPU资源
}
System.out.println(Thread.currentThread().getName() + "-" + num);
}
}
指令重排:
-
什么是指令重排:计算机并不是按照所写的程序代码执行的。
源代码 -> 编译器优化重排 -> 指令并行重排 -> 内存系统重排 -> 执行。处理器在进行指令重排的时候,会考虑到数据之间的依赖性问题。
-
volatile可以避免指令重排!
内存屏障。CPU指令。作用:a. 保证特定操作的执行顺序。
b. 可以保证某些变量的内存可见性。(利用这些特性,可以保证volatile的可见性。)

Volatile保证可见性,不保证原子性,由于内存屏障,可以防止指令重排。
内存屏障在单例模式使用较多。
18. 单例模式
饿汉式,DCL懒汉式。枚举为什么可以避免单例模式被破坏。
饿汉式
package com.huathy.single;
/**
* @ClassPath: com.huathy.single.Demo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 16:48
*/
/**
* 单例模式:饿汉式
* 饿汉式问题:会造成资源浪费
*/
public class Hungry {
private final static Hungry HUNGRY = new Hungry();
//构造方法私有
private Hungry(){}
public static Hungry getInstance(){
return HUNGRY;
}
}
DCL懒汉式
package com.huathy.single;
/**
* @ClassPath: com.huathy.single.Lazy
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 16:53
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* 单例模式:懒汉式。
*
*/
public class Lazy {
// private static Lazy lazy;
private volatile static Lazy lazy;
private static boolean flag = false;
private Lazy(){
synchronized (Lazy.class){
// if(lazy != null){
// throw new RuntimeException("不要试图使用反射破坏单例");
// }
if( flag == false ){
flag = true;
}else{
throw new RuntimeException("不要试图使用反射破坏单例");
}
System.out.println(Thread.currentThread().getName() + " 创建");
}
}
// 在并发下,这么写还是存在多次创建的问题
// public static Lazy getInstance(){
// if(lazy == null){
// lazy = new Lazy();
// }
// return lazy;
// }
//双重检测锁模式,DCL懒汉式单例
public static Lazy getInstance(){
if(lazy == null){
synchronized (Lazy.class){
if(lazy == null){
//问题:不是原子性操作
lazy = new Lazy();
/**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把这个对象指向这个空间。
* 如果这时发生指令重排:执行顺序由123 -> 132
* 多线程情况下B线程,在判断时不为空,直接返回lazy。但是由于lazy还未完成构造。
* 所以必须给lazy加上volatile关键字来防止指令重排
*/
}
}
}
return lazy;
}
/**
* 反射 可以破坏单例模式
* 测试输出:可见创建了两次,并且两次的结果并不一致。
* main 创建
* main 创建
* com.huathy.single.Lazy@14ae5a5
* com.huathy.single.Lazy@7f31245a
* 解决方案:
* 在构造方法中进行加锁判断。问题:如果都使用反射进行创建?则可以在构造方法中加上flag判断
*/
public static void main(String[] args) throws Exception {
Lazy lazy = Lazy.getInstance();
Field field = Lazy.class.getDeclaredField("flag");
field.setAccessible(true);
//1. 获取反射对象 2.获取无参的构造器
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
//设置构造器可见,无视私有构造器
declaredConstructor.setAccessible(true);
//通过反射创建对象
field.set(lazy,false);
Lazy lazy2 = declaredConstructor.newInstance();
Lazy lazy3 = declaredConstructor.newInstance();
System.out.println(lazy);
System.out.println(lazy2);
System.out.println(lazy3);
}
// public static void main(String[] args) {
// 测试
// for (int i = 0; i < 10; i++) {
// new Thread( ()->{
// Lazy.getInstance();
// } ).start();
// }
// }
}
静态内部类创建
package com.huathy.single;
/**
* @ClassPath: com.huathy.single.Holder
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 17:07
*/
/**
* 静态内部类
*/
public class Holder {
private Holder(){}
public static Holder getInstance(){
return InnerCalss.HOLDER;
}
public static class InnerCalss{
private static final Holder HOLDER = new Holder();
}
}
反射导致单例不安全。枚举类
package com.huathy.single;
import java.lang.reflect.Constructor;
/**
* @ClassPath: com.huathy.single.EnumSingle
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 17:39
*/
public enum EnumSingle {
INSTANCE ;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println(instance);
System.out.println(instance2);
//测试-> enum是单例的。
//尝试破坏单例模式:异常 java.lang.IllegalArgumentException: Cannot reflectively create enum objects
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
}
}
枚举反编译源码
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
package com.huathy.single;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/huathy/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
19. CAS
什么是CAS
CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
缺点:
- 自旋锁循环消耗时间
- 一次只能保证一个共享变量的原子性
- 会存在ABA问题
package com.huathy.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassPath: com.huathy.cas.CASDemo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 23:51
*/
public class CASDemo01 {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//CAS compareAndSet:比较并交换。参数(期望,更新)
// public final boolean compareAndSet(int expect, int update);
//如果达到期望值,则更新,否则不更新。CAS是CPU的并发原语。
System.out.println(atomicInteger.compareAndSet(2020,2021));
System.out.println(atomicInteger.getAndIncrement());
System.out.println(atomicInteger.get());
}
}
Unsafe类
由于Java无法操作内存,但是Java可以通过native来调用本地C++方法操作内存。


CAS:ABA问题
B线程由于速度较快,在A还没有反应的时候,修改了内存的值。

package com.huathy.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassPath: com.huathy.cas.CASDemo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 23:51
*/
public class CASDemo01 {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//CAS compareAndSet:比较并交换。参数(期望,更新)
// 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,6666));
System.out.println(atomicInteger.get());
}
}
解决方案:原子引用!
20. 原子引用
带版本号的原子操作。可解决ABA问题。
package com.huathy.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @ClassPath: com.huathy.cas.CASDemo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-17 23:51
*/
public class CASDemo02 {
public static void main(String[] args) {
/**
* 类比乐观锁原理。
* AtomicStampedReference:如果泛型是一个包装类,则需要注意对象的引用问题。
* 这里的值不能写的太大。因为Integer对于在-128~127之间的值是在IntegerCache.cache中产生,会复用已有对象,可使用==比较。
* 但之外的数据都会在堆中产生,并且不会服用已有对象。则需要使用equals比较。
*/
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);
new Thread( ()->{
//获得版本号
System.out.println("A1 ==> " + atomicStampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("A2 ==> " + atomicStampedReference.getStamp());
System.out.println(
atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("A3 ==> " + atomicStampedReference.getStamp());
} ,"A" ).start();
new Thread( ()->{
int stamp = atomicStampedReference.getStamp(); //获得版本号
System.out.println("B1 ==> " + atomicStampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
atomicStampedReference.compareAndSet(1, 6,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("B2 ==> " + atomicStampedReference.getStamp());
} ,"B" ).start();
}
}
21. 锁
公平锁,非公平锁
公平锁:非常公平,不能插队,必须先来后到,顺序执行。抢占式。
非公平锁:不公平的,可以插队(默认都是非公平锁)非抢占式。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁(递归锁)
Synchronized
package com.huathy.lock;
/**
* @ClassPath: com.huathy.lock.Demo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-18 13:55
*/
//Synchronized
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread( ()->{
phone.sms();
} ,"A" ).start();
new Thread( ()->{
phone.sms();
} ,"B" ).start();
}
}
class Phone{
//sms的锁需要等到call方法的锁释放后才会释放。
public synchronized void sms(){
System.out.println(Thread.currentThread().getName() + " sms");
call(); //这里也有锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName() + " call");
}
}
Lock
package com.huathy.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassPath: com.huathy.lock.Demo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-18 13:55
*/
//Synchronized
public class Demo02 {
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();
//sms的锁需要等到call方法的锁释放后才会释放。
public synchronized void sms(){
//lock.lock(); lock.unlock();锁必须配对,否则可能死锁
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();
}
}
}
自旋锁 spinLock
package com.huathy.lock;
/**
* @ClassPath: com.huathy.lock.SpinLockDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-18 14:15
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁,使用CAS操作
*/
public class SpinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "==> Lock");
while (!atomicReference.compareAndSet(null,thread)){
}
}
// 解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "==> UnLock");
atomicReference.compareAndSet(thread,null);
}
}
class Test{
public static void main(String[] args) {
SpinLockDemo lock = new SpinLockDemo();
new Thread( ()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"A" ).start();
new Thread( ()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"B" ).start();
}
}
死锁
死锁:资源互斥,请求保持,不可抢占,循环等待。

package com.huathy.lock;
import java.util.concurrent.TimeUnit;
/**
* @ClassPath: com.huathy.lock.DeadLockDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-18 14:43
*/
public class DeadLockDemo {
public static void main(String[] args) {
String a = "A";
String b = "B";
new Thread(new MyThread(a,b),"T1" ).start();
new Thread(new MyThread(b,a),"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 + " try get=>" + lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName() + " lock:" + lockB + " try get=>" + lockA);
}
}
}
}
死锁排查
-
使用
jps -l定位进程号

-
使用
jstack 进程号查看进程信息,查看死锁问题

本文来自博客园,作者:Huathy,遵循 CC 4.0 BY-NC-SA 版权协议。转载请注明原文链接:https://www.cnblogs.com/huathy/p/17253841.html



浙公网安备 33010602011771号