JMM与JUC

1.JMM

问题1:请你谈谈你对Volatile的理解

Volatile 是java虚拟机提供轻量级的同步机制

1. 保证可见性

2. 不保证原子性

3. 禁止指令重排

1.1、什么是JMM

JMM Java内存模型  不存在的东西,概念!约定 !

1.2、关于JMM的一些同步的约定:

1、线程解锁前,必须把共享变量立刻刷回主存,

2、线程枷锁前,必须读取主存中的最新值到工作内存中

3、加锁和解锁的是同一把锁

线程工作内存,主内存。store和write换一个位置

2.Volatile

2.1.保证可见性

package com.kuang.tvolatile;import java.util.concurrent.TimeUnit;public class Demo01 {//不加volatile ,程序就会陷入死循环,加了保证主内存的可见性public volatile static int num=0;public static void main(String[] args) {new Thread(()->{//线程对主内存的num值的变化是不知道的while (num==0){}System.out.println(num);}).start();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}num=1;System.out.println(num);}
}

2.2不保证原子性

package com.kuang.tvolatile;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;//不保证原子性
public class Demo02 {//volatile不保证原子性private volatile static int num=0;//改变就会重新加载主存的值,不改变就存一次到工作内存,工作内存相当于线程私有的栈帧,先这么理解public  static void  add(){num++;}
//    public   void  add1(){
//        lock.lock();
//        try {
//            num++;
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            lock.unlock();
//        }
//    }public static void main(String[] args) {//理论上num结果应该为两万for (int i = 0; i < 20; i++) {new Thread(()->{for (int j = 0; j < 1000; j++) {add();}}).start();}
//        Demo02 demo02 = new Demo02();
//        for (int i = 0; i < 10; i++) {
//            new Thread(()->{
//                for (int j = 0; j < 1000; j++) {
//                  demo02.add1();
//                }
//            }).start();
//        }while (Thread.activeCount()>2){//main gcThread.yield();}System.out.println(Thread.currentThread().getName()+"   "+num);}}

如果不加lock和synchronized,怎么样保证原子性?

使用原子类,解决原子性问题

package com.kuang.tvolatile;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;//不保证原子性
public class Demo02 {//volatile不保证原子性
//    private volatile static int num=0;public volatile static AtomicInteger num=new AtomicInteger();
//改变就会重新加载主存的值,不改变就存一次到工作内存,工作内存相当于线程私有的栈帧,先这么理解public  static void  add(){num.getAndIncrement();//AtomicInterger+1方法,CAS}
//    public   void  add1(){
//        lock.lock();
//        try {
//            num++;
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            lock.unlock();
//        }
//    }public static void main(String[] args) {//理论上num结果应该为两万for (int i = 0; i < 20; i++) {new Thread(()->{for (int j = 0; j < 1000; j++) {add();}}).start();}
//        Demo02 demo02 = new Demo02();
//        for (int i = 0; i < 10; i++) {
//            new Thread(()->{
//                for (int j = 0; j < 1000; j++) {
//                  demo02.add1();
//                }
//            }).start();
//        }while (Thread.activeCount()>2){//main gcThread.yield();//Thread.yield() 方法是Java中的一个静态方法,用于提示调度程序当前线程愿意放弃其当前时间片。// 换句话说,它建议线程愿意暂停执行并为同一优先级的其他线程提供机会。}System.out.println(Thread.currentThread().getName()+"   "+num);}}

这些类的底层都直接和操作系统直接挂钩 !在内存中修改值!!!Unsafe类是一个很特殊的存在!

2.3 、指令重排

什么是  指令重排: 你写的程序,计算机并不是按照你写的那样去执行的。

源代码->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行

volatile 是可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!

问:volatile在哪里避免指令重排的现象用的最多,在单例模式!!!

3.彻底玩转单例模式

饿汉式,DCL懒汉式

饿汉式

package com.kuang.single;//饿汉式
public class Hungry {private byte[] bytes1=new byte[1024*1024];private byte[] bytes2=new byte[1024*1024];private byte[] bytes3=new byte[1024*1024];private byte[] bytes4=new byte[1024*1024];private Hungry(){}private final static Hungry HUNGRY= new Hungry();public static  Hungry getHungry(){return HUNGRY;}}

DCL懒汉式 DCL:双重检测模式

原子性
package com.kuang.single;
//懒汉式
public class LazyMan {private LazyMan(){System.out.println(Thread.currentThread().getName()+"ok");}//双重检测锁,volatile内存屏障,防止指令重排,private volatile static LazyMan lazyMan;//双重检测该模式的 懒汉式单例  DCL懒汉式public static LazyMan getInstance(){if (lazyMan==null){synchronized (LazyMan.class){if (lazyMan==null){lazyMan=new LazyMan();//不是一个原子性操作/*** 1.分配内存空间* 2.执行构造方法,初始化对象* 3.把这个对象指向这个空间** 2和3的顺序,在指令重排的时候*/}//外面那一层判断空 是因为可以减少同步的时间,不会全部同步在锁当中,而一部分代码会因为不为空而直接获取对象。}}return lazyMan;}//多线程并发public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{LazyMan.getInstance();}).start();}}}

静态内部类

package com.kuang.single;
//静态内部类
public class Holder {private Holder(){}public static Holder getInstance(){return InnerClass.HOLDER;}public static class InnerClass{private static final Holder HOLDER=new Holder();}}

进阶

package com.kuang.single;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;//懒汉式
//道高一尺魔高一丈
public class LazyMan {private static boolean qingjiang=false;private LazyMan(){synchronized (LazyMan.class){if (qingjiang==false){qingjiang=true;}else {throw  new RuntimeException("不要尝试反射破坏单例模式");}}}//双重检测锁,原子性,volatile内存屏障,防止指令重排,private volatile static LazyMan lazyMan;//双重检测该模式的 懒汉式单例  DCL懒汉式public static LazyMan getInstance(){if (lazyMan==null){synchronized (LazyMan.class){if (lazyMan==null){lazyMan=new LazyMan();//不是一个原子性操作/*** 1.分配内存空间* 2.执行构造方法,初始化对象* 3.把这个对象指向这个空间** 2和3的顺序,在指令重排的时候*/}//外面那一层判断空 是因为可以减少同步的时间,不会全部同步在锁当中,而一部分代码会因为不为空而直接获取对象。}}return lazyMan;}//多线程并发
//    public static void main(String[] args) {
//        for (int i = 0; i < 10; i++) {
//            new Thread(()->{
//                LazyMan.getInstance();
//            }).start();
//        }
//    }public static void main(String[] args) throws Exception {
//        LazyMan instance = LazyMan.getInstance();Field qingjiang = LazyMan.class.getDeclaredField("qingjiang");qingjiang.setAccessible(true);Constructor<? extends LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);constructor.setAccessible(true);LazyMan instance2 = constructor.newInstance();qingjiang.set(constructor,false);LazyMan instance = constructor.newInstance();System.out.println(instance);System.out.println(instance2);}}

 

 

 

 反射无法破坏枚举

枚举在运行时没有无参构造,但是有一个有参构造,反射拿无参,会抛没有更多的方法

反射拿有参构造,则会抛出,

throw new IllegalArgumentException("Cannot reflectively create enum objects");

 

4.深入理解CAS

Unsafe类

 

5.原子引用

ABA问题

package com.kuang.cas;import java.util.concurrent.atomic.AtomicInteger;public class CASDemo {//CAS   atomicInteger.compareAndSet()  CAS就是compareAndSet的缩写:比较并交换!public static void main(String[] args){AtomicInteger atomicInteger=new AtomicInteger(2020);//对于我们平时写的SQL:乐观锁!//expect期望 update更新//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());//        atomicInteger.getAndIncrement();//期望的线程System.out.println(atomicInteger.compareAndSet(2020, 6666));System.out.println(atomicInteger.get());}}

6.各种锁的理解

 

package com.kuang.cas;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;public class CASDemo {//解决ABA问题,引入原子引用//CAS   atomicInteger.compareAndSet()  CAS就是compareAndSet的缩写:比较并交换!public static void main(String[] args){
//        AtomicInteger atomicInteger=new AtomicInteger(2020);AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(1, 1);new Thread(()->{int stamp = atomic.getStamp();//获取当前版本号System.out.println("a1=>"+stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("a2=>"+atomic.compareAndSet(1, 2, atomic.getStamp(), atomic.getStamp() + 1));System.out.println("a3=>"+atomic.compareAndSet(2, 1, atomic.getStamp(), atomic.getStamp() + 1));System.out.println("a4=>"+atomic.getStamp());},"a").start();//乐观锁原理是一样的new Thread(()->{int stamp = atomic.getStamp();//获取当前版本号System.out.println("b1=>"+stamp);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("b1=>"+(atomic.compareAndSet(1, 6, stamp, stamp + 1)));System.out.println("b2=>"+atomic.getStamp());},"b").start();//
//        //对于我们平时写的SQL:乐观锁!
//
//
//        //expect期望 update更新
//        //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());
//atomicInteger.getAndIncrement();
//
//        //期望的线程
//        System.out.println(atomicInteger.compareAndSet(2020, 6666));
//        System.out.println(atomicInteger.get());}}

对应的思想就是:乐观锁

6.1、公平锁与非公平锁

6.2、可重入锁

package com.kuang.reelock;//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{public synchronized void sms(){System.out.println(Thread.currentThread().getName()+"sms");call();//这里也有锁}public synchronized void call(){System.out.println(Thread.currentThread().getName()+"call");}}

 

package com.kuang.reelock.reelock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;//Synchronized
public class Demo02{public static void main(String[] args) {Phone1 phone = new Phone1();new Thread(()->{phone.sms();},"A").start();new Thread(()->{phone.sms();},"B").start();}
}class Phone1{Lock lock =new ReentrantLock();public  void sms(){lock.lock();lock.lock();try {System.out.println(Thread.currentThread().getName()+"sms");call();//这里也有锁} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();lock.unlock();}}public  void call(){lock.lock();try {System.out.println(Thread.currentThread().getName()+"call");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}

 

6.3、自旋锁

自定义锁

package com.kuang.spinlock;import java.util.concurrent.atomic.AtomicReference;//自旋锁
public class SpinLockDemo {/*** int 0* Thread null*/AtomicReference<Thread> atomicReference=new AtomicReference<>();//加锁public void lock(){Thread thread = Thread.currentThread();System.out.println("加锁的是"+thread.getName());//第一次进来 为null 成功改变,while条件不成立,不自旋,但是条件没有释放,第二次进来,他不是null了。返回false但是取反为true//自旋等待解锁while (!atomicReference.compareAndSet(null,thread)){}}//解锁public void unlock(){Thread thread = Thread.currentThread();System.out.println("解锁的是"+thread.getName());atomicReference.compareAndSet(thread,null);}//}

自定义锁测试

package com.kuang.spinlock;import java.util.concurrent.TimeUnit;public class TestDemo {public static void main(String[] args) {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(()->{spinLockDemo.lock();try {System.out.println(Thread.currentThread().getName()+"执行");TimeUnit.SECONDS.sleep(3);} catch (Exception e) {e.printStackTrace();} finally {spinLockDemo.unlock();}},"A").start();new Thread(()->{spinLockDemo.lock();try {System.out.println(Thread.currentThread().getName()+"执行");TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();} finally {spinLockDemo.unlock();}},"B").start();}
}

 

6.4、死锁

死锁是多线程或多进程并发执行时可能遇到的一种问题,它具有以下四个必要条件,也被称为死锁的四个特性:

1. 互斥条件(Mutual Exclusion):至少有一个资源被多个线程或进程竞争使用,且一次只能被一个线程或进程占用。当一个线程或进程占用了资源,其他线程或进程必须等待该资源释放。

2. 请求与保持条件(Hold and Wait):线程或进程在持有至少一个资源的同时,又请求其他线程或进程持有的资源。即线程或进程在等待其他资源时,仍然保持已经占有的资源。

3. 不可剥夺条件(No Preemption):已经被一个线程或进程占用的资源不能被其他线程或进程强制性地抢占,只能由占有者主动释放。

4. 循环等待条件(Circular Wait):存在一个线程或进程的资源请求序列,使得每个线程或进程都在等待下一个线程或进程所持有的资源。形成一个闭环,导致循环等待。

当以上四个条件同时满足时,就可能发生死锁。在死锁发生时,线程或进程无法继续执行,导致系统无法正常运行。因此,死锁的四个特性是死锁问题的根本原因,需要通过合理的资源分配和调度策略来避免死锁的发生。

package com.kuang.sisuo;import java.util.concurrent.TimeUnit;public class DeadLockDemo {public static void main(String[] args) {String A="A";String B="B";new Thread(new MyThread(A,B)).start();new Thread(new MyThread(B,A)).start();}}class MyThread implements Runnable {private String A;private String B;public MyThread(String a, String b) {A = a;B = b;}@Overridepublic void run() {synchronized (A){System.out.println(A+"获取"+B);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}synchronized (B){System.out.println(B+"获取"+A);}}}
}

如何解决问题 

栈:jstack

堆:  jprofile

调优 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/144959.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Java实现使用多线程,实现复制文件到另一个目录,起不一样的名字,创建100万个数据

目录 1 需求2 实现 1 需求 我现在有一个300MB 的文件&#xff0c;想要根据这个文件&#xff0c;创建100万个大小一样的&#xff0c;名称不一样&#xff0c;如何实现&#xff0c;如何比较快点实现 2 实现 1 先准备好这个文件 2 准备好目录 3 写代码 private static void crea…

Vue之ElementUI实现登陆及注册

目录 ​编辑 前言 一、ElementUI简介 1. 什么是ElementUI 2. 使用ElementUI的优势 3. ElementUI的应用场景 二、登陆注册前端界面开发 1. 修改端口号 2. 下载ElementUI所需的js依赖 2.1 添加Element-UI模块 2.2 导入Element-UI模块 2.3 测试Element-UI是否能用 3.编…

12款爆款项目管理工具推荐

项目管理需要用到的工具有哪些&#xff1f;从操作难易程度、功能是否涵盖项目需求、价格等方面入手推荐Zoho Projects、Redmine、Teambition、Microsoft Project、Omniplan、Podio、Freedcamp、Teamweek、Gantt Project、Basecamp、Meister Task、Teamwork等12款项目管理工具。…

DirectX 12 学习笔记 -结构

上篇文章我们创建了一个窗口&#xff0c;看样子还不难&#xff0c;我们继续玩DX12 引用一些文件 头文件 #include <d3d12.h> #include <dxgi1_4.h> #include <wrl.h>还有一些库 #pragma comment(lib, "d3d12.lib") #pragma comment(lib, "…

V4L2 驱动架构介绍

V4L2 简介 Video for Linux two(Video4Linux2)简称 V4L2&#xff0c;是 V4L 的改进版。V4L2 是 linux操作系统下用于视频和音频数据采集设备的驱动框架&#xff0c;为驱动和应用程序提供了一套统一的接口规范。 在 Linux 下&#xff0c;所有外设都被看成一种特殊的文件&#xf…

python使用mitmproxy和mitmdump抓包之拦截和修改包(四)

我认为mitmproxy最强大的地方&#xff0c;就是mitmdump可以结合python代码&#xff0c;灵活拦截和处理数据包。 首先&#xff0c;mitmdump的路径如下&#xff1a;&#xff08;使用pip3 install mitmproxy安装的情况&#xff0c;参考我的文章python使用mitmproxy和mitmdump抓包…

红黑树是如何实现的?

文章目录 一、红黑树的概念二、红黑树的性质三、红黑树和AVL树对比四、红黑树的插入1. 红黑树的结点定义2. 父亲的颜色3. 叔叔的颜色为红色4. 叔叔不存在5. 叔叔存在且为黑6. 插入的抽象图 五、红黑树的验证1. 检查平衡2. 计算高度与旋转次数3. 验证 六、 红黑树与AVL树的比较 …

基于单片机的煤气泄漏检测报警装置设计

一、项目介绍 煤气泄漏是一种常见的危险情况&#xff0c;可能导致火灾、爆炸和人员伤亡。为了及时发现煤气泄漏并采取相应的安全措施&#xff0c;设计了一种基于单片机的煤气泄漏检测报警装置。 主控芯片采用STM32F103C8T6作为主控芯片&#xff0c;具有强大的计算和控制能力。…

[H5动画制作系列 ]变量,帧频,监听器等的生命周期基础测试

模式:按照上述抓图,actions层&#xff0c;1帧,写初始化代码,10帧写返回代码到2帧代码,2-10帧之间一直循环。1帧及10帧代码如下&#xff1a; 如果程序在1-10之间循环,会反复创建变量i,多个监听器等。所以,第一帧最好执行一次即可&#xff0c;程序在2-10帧之间一直循环。

MySQL Installer is running in Community mode

每天很准时的弹出&#xff1a; 这是由于检查MySql并且更新的一个定时任务&#xff0c;没有更新成功导致 解决办法&#xff1a;禁用定时任务 1.先关闭错误框 2.打开控制面板 &#xff0c;使用小图标查看 3. 打开管理工具&#xff0c;双击打开任务计划程序 4.双击进入&#xf…

epoll与socket缓冲区的恩恩怨怨

文章目录 前言一、什么是socket缓冲区二、阻塞与非阻塞内核缓冲区1、如果发送缓冲区满了会怎么样阻塞非阻塞 2、如果接受缓冲区为空会怎么样阻塞非阻塞 三、epoll与缓冲区的恩恩怨怨水平触发边缘触发非阻塞阻塞 结论 前言 本文深挖网络编程中的缓冲区&#xff0c;从什么是缓冲…

数据结构 | 树

树 树是n&#xff08;n>0&#xff09;个结点的有限集。当n 0时&#xff0c;称为空树。在任意一棵非空树中应满足&#xff1a; 有且仅有一个特定的称为根的结点。当n>1时&#xff0c;其余节点可分为m&#xff08;m>0&#xff09;个互不相交的有限集T1,T2,…,Tm&#…

【Android】线程下载资源保证资源到位采用了 OkHttp的三方网络下载 文件缓存策略

背景 使用 SVGA的三方的url播放方式会比较慢&#xff0c;至少延迟3s以上才会出现svga效果&#xff0c;所以改变策略&#xff1a;将线上的svga全部下载到本地进行播放&#xff0c;那么就得将采用网络缓存的方式实现效果。 实现 那么就得实现以下几点&#xff1a; 初次下载缓…

专栏更新情况:华为流程、产品经理、战略管理、IPD

目录 前言 01 华为流程体系入门课 CSDN学院 02 产品经理进阶课 CSDN学院 03 BLM 战略方法论进阶课 04 IPD 进阶 100 例专栏 作者简介 前言 已上线四大课程专栏更新情况&#xff1a; 01 华为流程体系入门课&#xff08;视频图文&#xff09;&#xff1b; 02 硬件产品经…

UE4/5数字人MetaHuman通过已有动画进行修改

目录 通过已有动画修改动画 开始制作 创建一个关卡序列 将动画序列烘焙到控制绑定 打开我们自己创建的动画序列 之后便是烘焙出来 通过已有动画修改动画 首先架设我们已经有相关的MetaHuman的动画&#xff0c;但是这个动画因为是外部导入进来的&#xff0c;所以可能会出…

处理conda安装工具的动态库问题——解决记录 libssl.1.0.0 系统中所有openssl位置全览 whereis openssl

处理conda安装工具的动态库问题——解决记录 处理conda安装工具的动态库问题——解决记录 - 简书 解决libssl.so.1.0.0: cannot open shared object file: No such file or directory问题 - 简书 openssl 默认版本问题&#xff08;Anaconda相关&#xff09;_anaconda openssl-…

web:[极客大挑战 2019]Upload

题目 页面显示为一个上传&#xff0c;猜测上传一句话木马文件 先查看源代码看一下有没有有用的信息&#xff0c;说明要先上传图片&#xff0c;先尝试上传含有一句话木马的图片 构造payload <?php eval($_POST[123]);?> 上传后页面显示为&#xff0c;不能包含<&…

架构案例2022(四十二)

促销管理系统 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。在项目立项之初&#xff0c;公司领导层一致认为本次升级的主要目标是提升会员管理方式的灵活性&#xff0c;由于当前用户规模不大&#xff0c;业务也相对…

【笔记】离线Ubuntu20.04+mysql 5.7.36 + xtrabackup定时增量备份脚本

一、环境 ● Ubuntu版本查看 lsb_release -a● mysql 版本查看 mysql --version我的是ubuntu 20.04&#xff0c;mysql是5.7.36&#xff0c;所以要用 install_percona-xtrabackup-24 二、原理 备份 通过ubuntu自带的定时器运行增量备份脚本备份文件可以存储在映射后的其他…

云原生Kubernetes:Pod控制器

目录 一、理论 1.Pod控制器 2.Deployment 控制器 3.SatefulSet 控制器 4.DaemonSet 控制器 5.Job 控制器 6.CronJob 控制器 二、实验 1.Deployment 控制器 2.SatefulSet 控制器 3.DaemonSet 控制器 4.Job 控制器 5.CronJob 控制器 三、问题 1. showmount -e 报错…