java基础之线程安全问题以及线程安全集合类

本文探讨了线程安全问题的背景,如何通过互斥锁和`synchronized`关键字来解决,以及JDK5.0引入的无锁机制在ConcurrentHashMap和CopyOnWrite系列集合类中的应用,重点讲解了同步方法和同步代码块的区别。
摘要由CSDN通过智能技术生成

线程安全问题

  • 当多个线程同时访问同一个临界资源时,原子操作可能被破坏,会导致数据丢失, 就会触发线程安全问题

    • 临界资源: 被多个线程同时访问的对象

    • 原子操作: 线程访问临界资源的过程中不可更改和缺失的操作

互斥锁

  • 每个对象都默认拥有互斥锁, 该锁默认不开启.

  • 当开启互斥锁之后,线程想要访问对象,则在需要拥有时间片的基础上也拥有锁标记,锁标记只能被一个线程拥有,拥有时间片和锁标记的线程才能执行自身内容,在此期间,其他线程只能等正在执行的线程执行结束释放锁标记和时间片之后才能进入就绪状态

  • synchronized: 开启互斥锁的关键字

同步方法
  • 思路: 在被线程同时访问的方法上加锁

    访问修饰符 synchronized 返回值类型 方法名(参数列表){   
    
      }
    
    package com.by.util;
    ​
    import java.util.ArrayList;
    import java.util.List;
    ​
    /**
     * 工具类-操作集合属性
     */
    public class MyList {
        private List<Integer> list = new ArrayList<>();
    ​
        /**
         * 给集合属性添加元素
         * @param n 添加的元素值
         synchronized: 同步方法
         */
        public synchronized void insert(int n){
            list.add(n);
        }
        /**
         * 查看集合内容
         */
        public void query(){
            System.out.println("集合长度: " + list.size());
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+"  ");
            }
        }
    ​
    }

同步代码块
  • 思路: 让参与临界资源对象访问的线程自身加锁

    synchronized(临界资源对象){    //需要被认定为原子操作的代码 }
    
  • 使用: 所有访问同一临界资源的线程都需要同时添加同步代码块

    package com.by.test2;
    ​
    import com.by.util.MyList;
    ​
    public class TestMyList {
        public static void main(String[] args)throws Exception {
            //创建两个线程,同时操作工具类,线程1负责往集合中添加元素1-5,线程2负责往集合中添加元素6-10
            //添加结束之后查看集合内容
            //创建工具类对象
            MyList m = new MyList();
            Thread t1=new Thread(()->{
                for (int i = 1; i <=5 ; i++) {
                    synchronized (m) {
                        m.insert(i);
                    }
                }
            });
    ​
            Thread t2=new Thread(()->{
                for (int i = 6; i <=10 ; i++) {
                    synchronized (m) {
                        m.insert(i);
                    }
                }
            });
    ​
            t1.start();
            t2.start();
            //使t1和t2线程先进行添加操作
            t1.join();
            t2.join();
    ​
            //查看集合元素
            m.query();
        }
    }
    ​
    ​
    /*
    * 张三上厕所
    * 李四上厕所
    *
    * 原子操作: 脱裤子-->蹲下来-->上厕所-->擦屁股-->穿裤子-->冲水-->走人
    *
    *临界资源: 厕所-坑位
    *
    *解决方式1:给厕所大门加锁
    *解决方式2:自己给坑位加锁
    * 
    *
    * */
区别
  1. 同步方法: 线程执行需要同时争抢时间片和锁标记,写法简单但效率较慢

  2. 同步代码块: 线程只需要争抢时间片, 开启互斥锁的线程默认拥有锁标记, 效率较快但写法相对繁琐

线程安全的集合类

悲观锁: 悲观的认为集合一定会出现线程安全问题,所以直接加锁

乐观锁: 乐观的认为集合一定不会出现线程安全问题,如果安全问题发生,再利用算法解决问题(无锁机制)

JDK5.0,发布了一批无锁机制的线程安全的集合类

都来自于java.util.concurrent包

  1. ConcurrentHashMap: CAS算法

    compare and swap: 比较并交换

    原有值,预期值,结果值: 当原有值与预期值相等时才会将结果值放入内存

    int i=1;

    i++;

    原有值: 1 预期值: 1 结果值:2

  2. CopyOnWriteArrayList:

    • 当集合进行写(增删改)操作时,会先复制出一个副本,在副本中进行写操作,如果过程中出现线程安全问题,则舍弃当前副本,重新复制新的副本重复操作,直至副本中无异常,再将集合引用地址转换向副本地址,一次确保原集合中一定不会发生安全问题

    • 特点: 舍弃写的效率提高读的效率,适用于读操作远多于写操作时

  3. CopyOnWriteArraySet:

    • 原理与CopyOnWriteArrayList一致, 在写时会对元素进行去重

掌握

  1. 什么是线程安全问题?

  2. 如何解决线程安全问题

  3. 同步方法和同步代码块的区别

  4. 线程安全的集合类及原理

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

这孩子叫逆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值