千家信息网

Java并发编程中多线程高并发的知识点有哪些

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,今天小编给大家分享一下Java并发编程中多线程高并发的知识点有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获
千家信息网最后更新 2025年01月18日Java并发编程中多线程高并发的知识点有哪些

今天小编给大家分享一下Java并发编程中多线程高并发的知识点有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

1.JMM数据原子操作

  • read(读取)∶从主内存读取数据

  • load(载入):将主内存读取到的数据写入工作内存

  • use(使用):从工作内存读取数据来计算

  • assign(赋值):将计算好的值重新赋值到工作内存中

  • store(存储):将工作内存数据写入主内存

  • write(写入):将store过去的变量值赋值给主内存中的变量

  • lock(锁定):将主内存变量加锁,标识为线程独占状态

  • unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量

2.来看volatile关键字

(1)启动两个线程

public class VolatileDemo {     private static boolean flag = false;    public static void main(String[] args) throws InterruptedException {        new Thread(() -> {            while (!flag){            }            System.out.println("跳出while循环了");        }).start();         Thread.sleep(2000);        new Thread(() -> changeFlage()).start();    }     private static void changeFlage() {        System.out.println("开始改变flag值之前");        flag = true;        System.out.println("改变flag值之后");    }}

没加volatile之前,第一个线程的while判断一直满足

(2)给变量flag加了volatile之后

public class VolatileDemo {     private static volatile boolean flag = false;    public static void main(String[] args) throws InterruptedException {        new Thread(() -> {            while (!flag){            }            System.out.println("跳出while循环了");        }).start();         Thread.sleep(2000);        new Thread(() -> changeFlage()).start();    }     private static void changeFlage() {        System.out.println("开始改变flag值之前");        flag = true;        System.out.println("改变flag值之后");    }}

while语句能够满足条件


(3)原理解释:

开启第一个线程时,flag变量通过read从主内存中读出数据,使用load把数据加载进线程一的工作内存,通过use把flag读取到线程中;线程二也是同样的读取操作。线程二通过assign改变了flag的值,线程二工作内存中存储的flag=true,再通过store把flag写入到总线,总线再把flag通过write写入到住内存;由于两个线程读取操作的都是各种工作内存中的值,是主内存的副本,相互不通信,所以线程一一直再循环,线程一的flag为false。

加了volatile后,添加了缓存一致性协议(MESI),CPU通过总线嗅探机制感知到数据的变化而自己缓存里的值失效,此时线程一会把工作内存中存放的flag失效,从主内存中重新读取flag的值,此时满足while条件。

volatile底层通过汇编语言的lock修饰,当变量有修改立马写回主类,避免指令重排序


3.并发编程三大特性

可见性,有序性、原子性

4.双锁判断机制创建单例模式

public class DoubleCheckLockSinglenon {     private static volatile DoubleCheckLockSinglenon doubleCheckLockSingleon = null;     public DoubleCheckLockSinglenon(){}       public static DoubleCheckLockSinglenon getInstance(){        if (null == doubleCheckLockSingleon) {            synchronized(DoubleCheckLockSinglenon.class){                if(null == doubleCheckLockSingleon){                    doubleCheckLockSingleon = new DoubleCheckLockSinglenon();                }            }        }        return doubleCheckLockSingleon;    }      public static void main(String[] args) {        System.out.println(DoubleCheckLockSinglenon.getInstance());     } }

当线程调用getInstance方法创建的时候,先判断是否为空,为空则把对象加上锁,否则多线程的情况会创建重复,再锁里面再次判断是否为空,当new一个对象的时候,先在内存分配空间,再执行对象的init属性赋零操作,再执行初始化赋值操作。

cpu为了优化代码执行效率,会对满足as-if-serial和happens-before原则的代码进行指令重排序,as-if-serial规定线程内的执行代码顺序不影响结果输出,则会进行指令重排;

happens-before规定一些锁的顺序,同一个对象的unlock需要出现下一个lock之前等。

所以为了防止new的时候,指令重排,先进行赋值再执行赋零操作情况,需要加上volatile修饰符,加上volatile修饰后,在new操作时会创建内存屏障,高速cpu不进行指令重排序,底层是lock关键字;内存屏障分为LoadLoad(读读)、storestore(写写)、loadstore(读写)、storeload(写读),底层是c++代码写的,c++代码再调用汇编语言

5.synchronized关键字

(1)没加synchronized之前

package com.qingyun; /** * Synchronized关键字 */public class SynchronizedDemo {     public static void main(String[] args) throws InterruptedException {         Num num = new Num();       Thread t1 = new Thread(() -> {            for (int i = 0;i < 100000;i++) {                num.incrent();            }        });        t1.start();         for (int i = 0;i < 100000;i++) {            num.incrent();        }        t1.join();        System.out.println(num.getNum());    }}
package com.qingyun; public class Num {     public int num = 0;     public void incrent() {        num++;    }     public int getNum(){        return num;    }}

输出结果不是我们想要的,由于线程和for循环同时去调加的方法,导致最后输出的结果不是我们想要的

(2)加上synchronized之后

public synchronized void incrent() {        num++;    } //或者   public  void incrent() {        synchronized(this){            num++;        }    }

输出的结果是我们想要的,synchronized关键字底层使用的lock,是重量级锁,互斥锁、悲观锁,jdk1.6之前的锁,线程会放到一个队列里面等待着执行

6.AtomicIntger原子操作

(1)给原子加1的操作,可以使用AtomicInteger实现,与synchronized相比,性能大大提升

public class Num {    // public int num = 0;    AtomicInteger atomicInteger = new AtomicInteger();     public  void incrent() {       atomicInteger.incrementAndGet(); //原子加1    }     public int getNum(){        return atomicInteger.get();    }}

AtomicInteger源码有一个value字段,使用volatile修饰,volatile底层使用lock修饰,保证多线程并发结果的正确

private volatile int value;

(2)atomicInteger.incrementAndGet()方法做的事情:先获取到value的值,给值加1,再使用旧的值和atomicInteger进行比较,相等了把newValue设置进去,由于使用多线程可能值会不相等的情况,所以使用while进行循环比对,相等了执行完才推出

while(true) {    int oldValue = atomicInteger.get();    int newValue = oldValue+1;    if(atomicInteger.compareAndSet(oldValue,newValue)){       break;    }}

(3)atomicInteger.compareAndSet比对完值后才设置新值的方式即为CAS:无锁、乐观锁、轻量级锁,synchroznied存在线程阻塞、上行文切换、操作系统调度比较费时;CAS一直循环比对执行,效率要高

(4)compareAndSetInt底层使用native修饰,底层是c++代码,实现了原子性问题,在汇编语言使用代码lock cmpxchqq保证了原子性,是缓存行锁

(5)ABA问题:线程一那到一个变量,线程二执行比较快,也拿到这个变量,把变量的值进行修改,再快速修改回原来的值,这样变量的值有过一次变化,线程一再去执行compareAndSet的时候,虽然值还是之前的没变,但是已经发生过变化了,出现ABA问题

(6)解决ABA问题就是给变量加版本,每次操作变量版本加1,JDK带版本的锁有AtomicStampedReference,这样就算变量被其它线程修改过再回复原值,版本号也是不一致的。

7.锁优化

(1)重量级锁会把等待的线程放到队列中,重量级锁锁定的是monitor,存在上下问切换的资源占用;轻量级锁若是线程太多,会存在自旋,耗费cpu

(2)jdk1.6之后,锁升级为无状态-》偏向锁(锁id指定)-》轻量级锁(自旋膨胀)-》重量级锁(队列存储)

(3)创建一个对象,此时对象为无状态,当启动了一个线程时,再创建一个对象时,启用偏向锁,偏向锁执行完之后不会释放锁;当再启用一个线程时,有两个线程来挣抢对象时,立马又偏向锁升级为轻量级锁;当再创建一个线程的来挣抢对象锁时,由轻量级锁升级为重量级锁

(4)分段CAS,底层有一个base记录变量值,当有多个线程类访问此变量是,base的值会分为多个cell,组成数组,每个cell对应一到多个线程的cas处理,避免了线程的自旋空转,这样还是轻量级锁,返回数据的时候,底层调用的是所有cell数组和base的加和

public class Num {     LongAdder longAdder = new LongAdder();     public  void incrent() {         longAdder.increment();    }     public long getNum(){       return longAdder.longValue();    }}
public long longValue() {        return sum();    }
public long sum() {        Cell[] as = cells; Cell a;        long sum = base;        if (as != null) {            for (int i = 0; i < as.length; ++i) {                if ((a = as[i]) != null)                    sum += a.value;            }        }        return sum;    }

以上就是"Java并发编程中多线程高并发的知识点有哪些"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

线程 内存 变量 对象 底层 数据 工作 代码 原子 知识 轻量 轻量级 循环 关键 关键字 指令 时候 结果 版本 篇文章 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 河南威圳互联网科技有限公司 成都小修网络技术有限公司怎样 分子动力学模拟服务器公司 为公安部提供网络技术的公司 服务器python 代码加密 华为网络技术思维导图 监控管理平台服务器 扬州智能化服务器 路由器的radius服务器ip是什么意思 台湾有没有高端服务器云主机 网络安全教育案例征文 西安互助盘软件开发 方舟进化生存主服务器连接失败 中文经济类数据库 临沂市弘纳网络技术有限公司 网络安全威胁主要包括流量型 乐亭有服务器吗 服务器卡不住怎么办 松江区数据链网络技术优势 方舟ARK服务器管理工具 手机有我的世界服务器 在线服务器安全扫描 sql如何连接pb数据库 主页 劫持 软件开发 教职工如何提高网络安全意识 农经三资软件数据库连接失败 网络安全竞赛证书模板 云服务器和本地服务器配置一样吗 网络安全推演题 扬州智能化服务器
0