高并发及线程安全

1.高并发及线程安全的概念

1.高并发:在某个时间点上,有多个线程同时访问某一个资源。例如:双十一,12306 , 秒杀
2.线程安全性问题:当多个线程无序的访问同一个资源(例如:同一个变量、同一数据库、同一个文件……),而且访问同一资源的代码不具有“原子性”,这时对这一资源的方法就会产生安全性问题——导致此资源最终的结果是错误。
3.高并发所产生的安全性问题主要表现:
1).可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
2).有序性:即程序执行的顺序按照代码的先后顺序执行。
3).原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行

2.高并发问题一_不可见性(重点)

产生的原因:定义一个变量。一个线程修改变量的值,另一个线程由于访问频率太快,导致一直使用本线程区内的变量副本,而没有实时的到主内存中获取变量的新值。

package com.itheima.demo01visible;

/*
    高并发问题一_不可见性(重点)
 */
public class MyThread extends Thread{
    //定义一个静态的成员变量,供所有的线程使用
    public static int a = 0;

    @Override
    public void run() {
        System.out.println("Thread-0线程开始执行线程任务了,睡醒2秒钟,等待主线程先执行!");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread-0线程2秒钟之后,睡醒了,把变量a的值修改为1");
        a=1;
        System.out.println("Thread-0线程执行线程任务结束了!"+a);
    }
}
package com.itheima.demo01visible;

public class Demo01Visible {
    public static void main(String[] args) {
        //创建Thread的子类对象
        MyThread mt = new MyThread();
        //调用start方法,开启一个新的线程,执行run方法
        mt.start();

        //主线程在开启Thread-0线程之后,会继续执行main方法中的代码
        System.out.println("主线程,执行一个死循环");
        while (true){
            if(MyThread.a==1){
                System.out.println("主线程判断变量a的值==1,结束死循环!");
                break;
            }
        }
    }
}

3.Java内存模型JMM(了解)

概述:JMM(Java Memory Model)Java内存模型,是java虚拟机规范中所定义的一种内存模型。
Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。所有的共享变量都存储于主内存。这里所说的变量指的是实例变量(成员变量)和类变量(静态成员变量)。不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。线程对变量的所有的操作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接访问对方工作内存中的变量,线程间变量的值的传递需要通过主内存完成。

在这里插入图片描述
4.高并发问题二_无序性

1).有序性:多行代码的编写(.java)顺序和编译(.class)顺序。
有些时候,编译器在编译代码时,为了提高效率,会对代码“重排”:

.java文件
int a = 10;		//第一行
int b = 20;		//第二行
int c = a * b;	//第三行

在执行第三行之前,由于第一行和第二行的先后顺序无所谓,所以编译器可能会对“第一行”和“第二行”进行代码重排:

.class
int b = 20;
int a = 10;
int c = a * b;

2).但在多线程环境下,这种重排可能是我们不希望发生的,因为:重排,可能会影响另一个线程的结果,所以我们不需要代码进行重排

在这里插入图片描述
5.高并发问题三_非原子性(重点)

原子:不可分割 100行代码是一个原子,线程执行100行代码不可以分开执行,要么都执行,要都不执行

需求:
1.定义多线程共享的静态变量money
2.Thread-0线程把money的值增加10000
3.main线程把money的值增加10000
4.查看money的最终结果

package com.itheima.demo02atomic;

/*
    高并发问题三_非原子性(重点)
    需求:
        1.定义多线程共享的静态变量money
        2.Thread-0线程把money的值增加10000
        3.main线程把money的值增加10000
        4.查看money的最终结果
 */
public class MyThread extends Thread{
    //1.定义多线程共享的静态变量money
    public static int money=0;

    @Override
    public void run() {
        //2.Thread-0线程把money的值增加10000
        System.out.println("Thread-0线程开始执行线程任务,增加money的值!");
        for (int i = 0; i < 10000; i++) {
            money++;
        }
        System.out.println("Thread-0线程执行线程任务结束!");
    }
}
package com.itheima.demo02atomic;

public class Demo01Atomic {
    public static void main(String[] args) throws InterruptedException {
        MyThread mt = new MyThread();
        mt.start();

        //主线程在开启新的线程之后,会继续执行main方法中的代码
        //3.main线程把money的值增加10000
        System.out.println("主线程开始增加变量money的值!");
        for (int i = 0; i < 10000; i++) {
            MyThread.money++;
        }
        System.out.println("主线程增加money值结束,睡眠2秒钟,等待Thread-0线程也执行完毕!");
        Thread.sleep(2000);
        System.out.println("最终两个线程都执行完毕,打印money的值:"+MyThread.money);
    }
}
主线程开始增加变量money的值!
主线程增加money值结束,睡眠2秒钟,等待Thread-0线程也执行完毕!
Thread-0线程开始执行线程任务,增加money的值!
Thread-0线程执行线程任务结束!
最终两个线程都执行完毕,打印money的值:20000
    
主线程开始增加变量money的值!
Thread-0线程开始执行线程任务,增加money的值!
主线程增加money值结束,睡眠2秒钟,等待Thread-0线程也执行完毕!
Thread-0线程执行线程任务结束!
最终两个线程都执行完毕,打印money的值:18198
    
主线程开始增加变量money的值!
Thread-0线程开始执行线程任务,增加money的值!
Thread-0线程执行线程任务结束!
主线程增加money值结束,睡眠2秒钟,等待Thread-0线程也执行完毕!
最终两个线程都执行完毕,打印money的值:12060  

原子性原理
在这里插入图片描述

每个线程访问money变量,都需要三步:
1).取money的值;
2).将money++
3).将money写回
这三步就不具有“原子性”——执行某一步时,很可能会被暂停(失去了cpu的执行权),执行另外一个线程,就会导致变量的最终结果错误!!!!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java高并发线程安全集合是指在多线程环境下能够保证数据一致性和线程安全的数据结构。Java提供了许多高并发线程安全集合,包括ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList、CopyOnWriteArraySet等。 ConcurrentHashMap是一个线程安全的哈希表,它允许多个线程同时读取并修改其中的元素。它使用分段锁的方式来实现并发访问,不同的线程可以同时访问不同的分段,从而提高了并发性能。 ConcurrentSkipListMap是一个基于跳表的并发有序映射,它可以提供较好的并发性能,且支持按照键的顺序进行遍历。它的实现是通过通过多层链表实现的,每一层链表中的节点按照键的顺序排列。 ConcurrentSkipListSet是一个基于ConcurrentSkipListMap的并发有序集合,它实现了Set接口,并且保证元素的有序性和线程安全性。 CopyOnWriteArrayList是一个线程安全的ArrayList,它通过每次修改时创建一个新的副本来实现线程安全。虽然在插入和删除操作时需要复制整个数组,但读取操作非常高效,适用于读操作远多于写操作的场景。 CopyOnWriteArraySet是一个线程安全的Set,它是基于CopyOnWriteArrayList实现的。它通过复制整个数组来实现线程安全,保证了元素的唯一性和线程安全。 这些高并发线程安全集合在多线程环境中保证了数据的一致性和线程安全性,能够提高并发性能和效率,适用于高度并发和需要频繁读写的场景。但需要注意的是,并发集合在某些操作上可能会损失一些性能,因此在选择使用时需根据具体需求进行权衡和选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旭子在努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值