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.hqwc.cn/news/120917.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GD32工程创建

1.创建空工程 在任意路径下创建空的test文件夹。打开keil5空工程创建空工程 选择对应的芯片型号&#xff1a; 然后把空工程保存到test文件夹下。会自动生成如下文件。 2. 添加组 下载GD32F10X的固件库&#xff1a;在百度里搜索GD32进入官网。 下载下来对应的文件如下&#xff…

Word | 简单可操作的快捷公式编号、右对齐和引用方法

1. 问题描述 在理工科论文的写作中&#xff0c;涉及到大量的公式输入&#xff0c;我们希望能够按照章节为公式进行编号&#xff0c;并且实现公式居中&#xff0c;编号右对齐的效果。网上有各种各样的方法来实现&#xff0c;操作繁琐和简单的混在一起&#xff0c;让没有接触过公…

Linux性能优化--性能工具-系统CPU

2.0.概述 本章概述了系统级的Linux性能工具。这些工具是你追踪性能问题时的第一道防线。 它们能展示整个系统的性能情况和哪些部分表现不好。 1.理解系统级性能的基本指标&#xff0c;包括CPU的使用情况。 2.明白哪些工具可以检索这些系统级性能指标。2.1CPU性能统计信息 为了…

LeetCode 接雨水 双指针

原题链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题面&#xff1a; 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a…

Java编程技巧:文件上传、下载、预览

目录 1、上传文件1.1、代码1.2、postman测试截图 2、下载resources目录中的模板文件2.1、项目结构2.2、代码2.3、使用场景 3、预览文件3.1、项目结构3.2、代码3.3、使用场景 1、上传文件 1.1、代码 PostMapping("/uploadFile") public String uploadFile(Multipart…

WebPack-打包工具

从图中我们可以看出&#xff0c;Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件&#xff0c;减少了页面的请求. 下面举个例子 &#xff1a; main.js 我们只命名导出一个变量 export const name"老六"index.js import { name } from "./tset/…

什么是Promise链(Promise chaining)?它在异步编程中的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是 Promise 链&#xff1f;⭐ 异步编程中的作用⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、…

【PDF】pdf 学习之路

PDF 文件格式解析 https://www.cnblogs.com/theyangfan/p/17074647.html 权威的文档&#xff1a; 推荐第一个连接&#xff1a; PDF Explained &#xff08;译作《PDF 解析》&#xff09; | PDF-Explained《PDF 解析》https://zxyle.github.io/PDF-Explained/ https://zxyle…

VBA技术资料MF61:按每行指定字符数自动换行

【分享成果&#xff0c;随喜正能量】人生的旅途&#xff0c;一时的失意&#xff0c;一时的挫折&#xff0c;跌了一跤&#xff0c;不是人生的全部&#xff0c;只是人生的一个逗点&#xff0c;未来的成就、光辉&#xff0c;还有很多的虚线有待你继续去完成。。 我给VBA的定义&am…

【前端】ECMAScript6从入门到进阶

【前端】ECMAScript6从入门到进阶 1.ES6简介及环境搭建 1.1.ECMAScript 6简介 &#xff08;1&#xff09;ECMAScript 6是什么 ECMAScript 6.0&#xff08;以下简称 ES6&#xff09;是 JavaScript 语言的下一代标准&#xff0c;已经在2015年6月正式发布了。它的目标&#xff…

asp.net core mvc Razor +dapper 增删改查,分页(保姆教程)

说明&#xff1a;本demo使用sqlserver数据库&#xff0c;dapper orm框架 完成一张学生信息表的增删改查&#xff0c;前端部分使用的是Razor视图&#xff0c; Linq分页 HtmlHelper。&#xff08;代码随便写的&#xff0c;具体可以自己优化&#xff09; //实现效果如下&#xff0…

macOS 下 Termius 中文显示为乱码

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…