javase基础学习第十天

今天学习了高并发和线程安全,以及怎么解决这些安全问题代码量相对较少,重要的是理解

时间: 2022/4/24

高并发和线程安全

高并发就是在同一时间段内,有大量的线程访问
比如: 双十一 12306春运购票

线程安全问题就是线程和线程之间出现了不该出现的相互影响

多线程内存运行机制
多线程在内存中和之前单线程不太一样
多个线程在内存中共享同一块堆空间和方法区
但是每个线程会自己有自己的一个栈空间
多线程的内存运行机制
可见性
在多个线程共同操作一个变量的时候,一个线程可能看不到另一个线程对变量的修改,这个叫可见性问题

public class AAAA1 extends Thread{
    static int a=0;
    @Override
    public void run() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a=10;
        System.out.println("AAAA1的a = " + a);
    }
}
public class AAAA2 extends Thread{
    @Override
    public void run() {
        while (true) {
            if (AAAA1.a==10) {
                System.out.println("AAAA2知道了a=10");
                break;
            }
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        AAAA1 aaaa1 = new AAAA1();
        aaaa1.start();
        AAAA2 aaaa2 = new AAAA2();
        aaaa2.start();
    }
}

结果:
可见性
原因:
可见性产生的原因
有序性
程序在编译期间,有可能会把没有逻辑关系的代码打乱顺序,这个现象叫代码重排,重排对单线程不会有影响,但是在多线程情况下就有可能对别的线程造成影响。
这个现象是随机的有出现的可能性,我们没有办法使用代码直接演示…
有序性问题
原子性
正常理解原子是不可分割的最小单位
但是java中在多线程的情况下,两个原子代码会互相产生影响,这个影响叫原子性问题

public class AAAA3 extends Thread{
    static int a = 0;
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            a++;
        }
    }
}
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        AAAA3 aaaa1 = new AAAA3();
        aaaa1.start();
        AAAA3 aaaa2 = new AAAA3();
        aaaa2.start();
        Thread.sleep(1000);
        System.out.println(AAAA3.a);
    }
}

原子性的结果

原因:
在这里插入图片描述
解决以上安全问题的办法
volatile关键字

volatile关键字可以作用于成员变量
作用:
可以解决可见性和有序性问题

volatile static int a = 0;

AtomicInteger关键字
对于整数int,java中有一个原子类整数叫AtomicInteger
AtomicInteger可以解决前面三种问题

//static int a = 0;
 static AtomicInteger a = new AtomicInteger(0);

AtomicInteger工作机制-CAS机制

AtomicIntegerArray关键字

普通数组比如int[] 在多线程的情况下也会出现原子性问题
我们可以使用原子类数组AtomicIntegerArray解决问题

    //定义静态数组
    //static int[] arr = new int[100];

    //多线程可以使用原子类的数组解决
    static AtomicIntegerArray arr = new AtomicIntegerArray(100);

多行代码的线程安全问题

用卖票问题来演示线程安全

public class MyThread extends Thread {
    //定义静态变量
    static int ticket = 100;
    @Override
    public void run() {
        while(true){
            if(ticket<=0){
                break;
            }
            System.out.println(getName() + "卖出了" + ticket + "号票");
            ticket--;
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        //开启线程
        MyThread mt = new MyThread();
        mt.setName("窗口一");
        mt.start();
        //开启线程
        MyThread mt2 = new MyThread();
        mt2.setName("窗口二");
        mt2.start();
    }
}

synchronized同步代码块
格式:


synchronized(锁对象){
    代码
}

流程:
同步代码块有一个锁对象,必须获取到锁才能进入代码块内部,只能有一个线程进入代码块,在线程出来之前,别的线程无法进入代码块。

锁对象:
java中任意类型的对象都可以放在同步代码块里面当做锁对象,只要是一个对象就可以
要求多个线程用的锁对象必须是同一个对象,如果不是同一个对象起不到效果

public class MyThread extends Thread {
    //定义静态变量
    static int ticket = 100;

    @Override
    public void run() {
        while(true){
            //同步代码块
            //锁对象两个要求:1.可以是任意类型  2.多个线程用的是同一个对象
            synchronized ("abc") {
                if (ticket <= 0) {
                    break;
                }
                System.out.println(getName() + "卖出了" + ticket + "号票");
                ticket--;
            }
        }
    }
}
//----------------------------------------------
public class Demo {
    public static void main(String[] args) {

        //开启线程
        MyThread mt = new MyThread();
        mt.setName("窗口一");
        mt.start();

        //开启线程
        MyThread mt2 = new MyThread();
        mt2.setName("窗口二");
        mt2.start();
    }
}

synchronized同步方法
同步方法锁对象是系统默认的,不用我们手动写…

格式:
定义一个同步方法:
public synchronized void method(){

	}
	同步方法的锁对象叫this
	
	
定义一个静态同步方法:
	public static synchronized void method(){
        
	}
	静态同步方法的锁对象叫 类的字节码文件对象 
	(一个类在第一次被使用时会被加载到方法区,一个类只会被加载一次 XXX.class)

Lock类型锁
Lock是一个锁类型,有两个方法
lock():加锁
unlock():解锁

public class MyThread extends Thread {
    //定义静态变量
    static int ticket = 100;
    //创建Lock对象
    static Lock c = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            //加锁
            c.lock();
            if(ticket<=0){
                //解锁
                c.unlock();
                break;
            }
            System.out.println(getName() + "卖出了" + ticket + "号票");
            ticket--;
            //解锁
            c.unlock();
        }
    }
}
-------------------------------------------------------------
public class Demo {
    public static void main(String[] args) {
        //开启线程
        MyThread mt = new MyThread();
        mt.setName("窗口一");
        mt.start();
        //开启线程
        MyThread mt2 = new MyThread();
        mt2.setName("窗口二");
        mt2.start();

    }
}

悲观锁和乐观锁
同步写法叫 悲观锁 ,不允许多个线程一起执行,解决了所有线程问题,但是效率低
CAS机制叫 乐观锁 , 允许多个线程一起执行,能解决原子性问题,效率比较高

开发中多线程如何避免安全问题
多线程问题出现需要条件:
1.要有多个线程
2.要有共享变量
3.多个线程在同时操作共享变量

今天我们学习是故意用了多线程操作共享数据,以后写代码我们尽量不要定义多个线程的共享变量,就不会出现问题…

并发包

之前我们的集合其实在多线程情况下也都有可能出问题
如果在多线程情况下需要使用共享集合,就需要使用并发包中的集合类型

ArrayList

ArrayList可能出现线程安全问题
CopyOnWriteArrayList可以解决线程安全问题

   //定义集合
    //static ArrayList<Integer> list = new ArrayList<>();
    static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();

HashSet
HashSet可能出现线程安全问题
CopyOnWriteArraySet可以解决线程安全问题

   //定义集合
    //static HashSet<Integer> set = new HashSet<>();
    static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();

HashMap
HashMap可能出现线程安全问题
Hashtable和ConcurrentHashMap都可以解决线程安全问题

    //定义集合
    //static HashMap<Integer,Integer> map = new HashMap<>();
    //static Hashtable<Integer,Integer> map = new Hashtable<>();
    static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();

并发包的类执行效率更高
Hashtable其实是一个比较老的解决线程安全的类,他的方法是同步方法,
一个线程在执行的时候,其他线程只能等待,所以效率很低

ConcurrentHashMap是并发包的类,他是同步代码块(同步了方法内的一部分代码)+CAS机制,也能解决线程安全问题,效率比较高

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

皇家小黄

创作不易!!!!

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

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

打赏作者

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

抵扣说明:

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

余额充值