• 这8种保证线程安全的技术你都知道吗?


    并发情况下如何保证数据安全,一直都是开发人员每天都要面对的问题,稍不注意就会出现数据异常,造成不可挽回的结果。笔者根据自己的实际开发经验,总结了下面几种保证数据安全的技术手段:

    1. 无状态

    2. 不可变

    3. 安全的发布

    4. volatile

    5. synchronized

    6. lock

    7. cas

    8. threadlocal

    一.无状态

    我们都知道只有多个线程访问公共资源的时候,才可能出现数据安全问题,那么如果我们没有公共资源,是不是就没有这个问题呢?

    public class NoStatusService {
        public void add(String status) {        System.out.println("add status:" + status);    }
        public void update(String status) {        System.out.println("update status:" + status);    }}

    二.不可变

    如果多个线程访问公共资源是不可变的,也不会出现数据的安全性问题。

    public class NoChangeService {    public static final String DEFAULT_NAME = "abc";
        public void add(String status) {        System.out.println("add status:" + status);    }}

    三.安全的发布

    如果类中有公共资源,但是没有对外开放访问权限,即对外安全发布,也没有线程安全问题​​​​​​​

    public class SafePublishService {    private String name;
        public String getName() {        return name;    }
        public void add(String status) {        System.out.println("add status:" + status);    }}

    四.volatile

    如果有些公共资源只是一个开关,只要求可见性,不要求原子性,这样可以用volidate关键字定义来解决问题。​​​​​​​

    public class FlagService {    public volatile boolean flag = false;
        public void change() {        if (flag) {            System.out.println("return");            return;        }              flag = true;        System.out.println("change");    }}

    五.synchronized

    使用JDK内部提供的同步机制,这也是使用比较多的手段,分为:方法同步 和 代码块同步,我们优先使用代码块同步,因为方法同步的范围更大,更消耗性能。每个对象内部都又一把锁,只有抢答那把锁的线程,才能进入代码块里,代码块执行完之后,会自动释放锁。​​​​​​​

    public class SyncService {    private int age = 1;
        public synchronized void add(int i) {        age = age + i;                System.out.println("age:" + age);    }
        public void update(int i) {        synchronized (this) {            age = age + i;                                      System.out.println("age:" + age);        }         }}

    六.lock

    除了使用synchronized关键字实现同步功能之外,JDK还提供了lock显示锁的方式。它包含:可重入锁、读写锁 等更多更强大的功能,有个小问题就是需要手动释放锁,不过在编码时提供了更多的灵活性。​​​​​​​

    public class LockService {    private ReentrantLock reentrantLock = new ReentrantLock();    public int age = 1;        public void add(int i) {        try {            reentrantLock.lock();            age = age + i;                       System.out.println("age:" + age);        } finally {            reentrantLock.unlock();                }       }}

    七.cas

    JDK除了使用锁的机制解决多线程情况下数据安全问题之外,还提供了cas机制。这种机制是使用CPU中比较和交换指令的原子性,JDK里面是通过Unsafe类实现的。cas需要四个值:旧数据、期望数据、新数据 和 地址,比较旧数据 和 期望的数据如果一样的话,就把旧数据改成新数据,当前线程不断自旋,一直到成功为止。不过可能会出现aba问题,需要使用AtomicStampedReference增加版本号解决。其实,实际工作中很少直接使用Unsafe类的,一般用atomic包下面的类即可。​​​​​​​

    public class AtomicService {    private AtomicInteger atomicInteger = new AtomicInteger();        public int add(int i) {        return atomicInteger.getAndAdd(i);    }}

    八.threadlocal

    除了上面几种解决思路之外,JDK还提供了另外一种用空间换时间的新思路:threadlocal。它的核心思想是:共享变量在每个线程都有一个副本,每个线程操作的都是自己的副本,对另外的线程没有影响。特别注意,使用threadlocal时,使用完之后,要记得调用remove方法,不然可能会出现内存泄露问题。​​​​​​​

    public class ThreadLocalService {    private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        public void add(int i) {        Integer integer = threadLocal.get();        threadLocal.set(integer == null ? 0 : integer + i);    }
    }

    总结

    本文介绍了8种多线程情况下保证数据安全的技术手段,当然实际工作中可能会有其他。技术没有好坏之分,主要是看使用的场景,需要在不同的场景下使用不同的技术。

     

  • 相关阅读:
    antd前端上传获取文件的md5, 并结果form表单提交
    JS哈希表系列
    nodejs+vue全国公考岗位及报考人数分析
    【C++】C 语言与 C++ 语言的关系 ( C 语言发展 | C 语言缺陷 | C 语言 + 面向对象 + 高级语言特性 | C++ 语言增加内容 | C 语言与 C++ 语言应用场景 )
    Linux-Shell命令行解释器的模拟实现
    【Java】Idea控制台中文乱码问题完全解决方案
    5G专网融合时间敏感网络架构技术
    003_IO 扩展(串转并)-74HC595
    不到200一个成长枪皮?成长枪皮返厂,普适性入手方案都在这了
    pyqt5_windows_hid_auto_detect_hotplug
  • 原文地址:https://blog.csdn.net/m0_72088858/article/details/126765015