当前位置:首页 > 科技  > 软件

到底什么是线程安全? 如何保证线程安全?

来源: 责编: 时间:2024-05-20 17:49:26 262观看
导读随着硬件技术的快速发展(比如多核处理器,超线程技术),我们通常会在代码中使用多线程(比如线程池)来提高性能,但是,多线程又会带来线程安全问题。因此,本文将深入探讨Java中的线程安全问题。1.什么是线程安全?首先,我们来看看维基

随着硬件技术的快速发展(比如多核处理器,超线程技术),我们通常会在代码中使用多线程(比如线程池)来提高性能,但是,多线程又会带来线程安全问题。因此,本文将深入探讨Java中的线程安全问题。DyQ28资讯网——每日最新资讯28at.com

DyQ28资讯网——每日最新资讯28at.com

1.什么是线程安全?

首先,我们来看看维基百科对线程安全是如何描述的,如下图:DyQ28资讯网——每日最新资讯28at.com

DyQ28资讯网——每日最新资讯28at.com

总结一下:线程安全(Thread Safety)是指多个线程访问共享资源时,不会破坏资源的完整性。如下图:DyQ28资讯网——每日最新资讯28at.com

DyQ28资讯网——每日最新资讯28at.com

请注意,导致线程安全问题一定要同时具备以下 3个条件,缺一不可:DyQ28资讯网——每日最新资讯28at.com

  • 多线程环境:如果是单线程,程序肯定会串行顺序执行,不可能出现线程安全问题。
  • 操作共享资源:所谓共享资源是指多个线程或进程可以同时访问和使用的资源。如果每个线程都是操作自己的局部变量,尽管满足条件1,但也不会出现线程安全问题。
  • 至少存在一个写操作:如果是多线程读取共享资源,尽管满足了前 2个条件,但是读操作天然是幂等的,因此也不会出现线程安全的问题,所以线程中至少存在一个写操作。

上面从表象上说明线程安全需要具备的 3个条件,在 Java中,线程安全性通常涉及以下 3个指标:DyQ28资讯网——每日最新资讯28at.com

  • 原子性(Atomicity):操作要么全部完成,要么全部不完成。
  • 可见性(Visibility):一个线程对共享变量的修改对其他线程是立即可见的。
  • 有序性(Ordering):程序的执行顺序符合预期,不会因为编译器优化或CPU重排序而改变。

2. 产生线程安全的根因

在 Java中,造成线程安全问题的根因是硬件结构,为了消除 CPU和主内存之间的硬件速度差,通常会在两者之间设置多级缓存(L1 ~ L3),如下图:DyQ28资讯网——每日最新资讯28at.com

DyQ28资讯网——每日最新资讯28at.com

Java为了适配这种多级缓存的硬件构造,设计了一套与之对应的内存模型(JMM,Java memory model,包括主内存和工作内存,如下图:DyQ28资讯网——每日最新资讯28at.com

DyQ28资讯网——每日最新资讯28at.com

  • 主内存:所有的变量都存储在主内存中。
  • 工作内存:每个线程都有自己的工作内存,会将主内存的共享变量复制到自己的工作内存中,然后做后续业务操作,最终再将工作内存中的变量刷新到主内存。

线程对变量的所有操作(读取、写入)都必须在工作内存中进行,而不能直接读写主内存中的变量。线程之间无法直接访问对方的工作内存,变量的传递需要通过主内存来完成。DyQ28资讯网——每日最新资讯28at.com

关于 Java内存模型的原理,我们会在另外的文章中单独讲解,本文只是概要性的总结。DyQ28资讯网——每日最新资讯28at.com

3. 原子性

在数据库事务ACID中也有原子性(Atomicity)的概念,它是指一个操作是不可分割的,即要么全部执行,要么全部不执行。Java线程安全中的原子性与数据库事务中的原子性本质是一样的,只是它们应用的上下文和具体实现有所不同。DyQ28资讯网——每日最新资讯28at.com

Java提供了多种方式来保证原子性,比如 同步块、锁或者原子类。DyQ28资讯网——每日最新资讯28at.com

为了更好的说明原子性,我们这里以一个反例来展示不具备原子性,如下代码:DyQ28资讯网——每日最新资讯28at.com

public class AtomicityTest {    private int i = 0;    public void increment() {        i++;    }}

DyQ28资讯网——每日最新资讯28at.com

在上述代码中,i++这种写法在我们的日常开发经常使用,但它不是一个原子操作,实际上i++分为三步:DyQ28资讯网——每日最新资讯28at.com

  • 读取i的值
  • 将i的值加 1
  • 将结果写回给i

如果多个线程同时执行increment()方法,可能会导致i的值不正确,比如有 3个线程A,B,C:DyQ28资讯网——每日最新资讯28at.com

  • 线程A读取i的值,并且将i的值加 1,但是还未将结果写回给i;
  • 此时,线程B读取i的值仍然是0,并且将i的值加 1;
  • 线程A 将结果写回给i,将i设置为 1;
  • 线程B 将结果写回给i,将i设置为 1;
  • 线程C 读取i的值为1,并且将i的值加 1,并且将结果写回给i,将i设置为 2;

3个线程都对i进行i++操作,预期i的最终值是 3,但因为i++无法保证原子性,因此,i最终的值未达到预期的值。DyQ28资讯网——每日最新资讯28at.com

4. 可见性

DyQ28资讯网——每日最新资讯28at.com

可见性是指一个线程对共享变量的修改,其他线程能立刻看到。在Java中,volatile关键字可以保证变量的可见性。DyQ28资讯网——每日最新资讯28at.com

为了更好的说明可见性,我们这里以一个示例进行分析,如下代码:DyQ28资讯网——每日最新资讯28at.com

public class VisibilityTest {    private boolean running = true;    public void stop() {        running = false;    }    public void run() {        while (running) {            // do something        }    }}

DyQ28资讯网——每日最新资讯28at.com

在上述代码中,变量running是一个全局变量,如果没有使用volatile关键字,running 变量的修改可能不会被其他线程立即看到。DyQ28资讯网——每日最新资讯28at.com

5. 有序性

有序性是指程序代码的执行顺序。在单线程环境中,代码的执行顺序通常是按照代码的书写顺序执行的。然而,在多线程环境中,编译器、JVM和CPU可能会为了优化性能进行指令重排序(Instruction Reordering),这可能会导致代码的执行顺序与预期不一致。DyQ28资讯网——每日最新资讯28at.com

Java内存模型(Java Memory Model, JMM)允许编译器和处理器进行指令重排序,但会保证单线程内的执行结果和多线程内的同步结果是正确的。DyQ28资讯网——每日最新资讯28at.com

这里以一个反例来展示不具备有序性,如下代码:DyQ28资讯网——每日最新资讯28at.com

public class ReorderingExample {private int x = 0;private boolean flag = false;    public void writer() {        x = 42;        flag = true;    }    public void read() {        if (flag) {            System.out.println(x); // 可能输出0        }    }}

DyQ28资讯网——每日最新资讯28at.com

在上述代码中,read()方法可能会看到flag=true,但x仍然为 0,因为编译器或CPU可能对指令进行重排序。DyQ28资讯网——每日最新资讯28at.com

6. 如何保证线程安全

在 Java中,通常可以通过以下几个方式来保证线程安全。DyQ28资讯网——每日最新资讯28at.com

(1) synchronized关键字DyQ28资讯网——每日最新资讯28at.com

synchronized是Java的一个原语关键字,它可以保证方法或代码块在同一时刻只能被一个线程执行,从而确保原子性和可见性。DyQ28资讯网——每日最新资讯28at.com

下面的代码是synchronized关键字的简单使用:DyQ28资讯网——每日最新资讯28at.com

public class SynchronizedTest {private int i = 0;    public synchronized void increment() {        i++;    }    public synchronized int getCount() {        return i;    }}

DyQ28资讯网——每日最新资讯28at.com

(2) Lock 接口DyQ28资讯网——每日最新资讯28at.com

Lock接口提供了比synchronized更灵活的锁机制,常用的实现类有 ReentrantLock 可重入锁。DyQ28资讯网——每日最新资讯28at.com

下面的代码是Lock关键字的简单使用:DyQ28资讯网——每日最新资讯28at.com

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class LockCounter {private int count = 0;private final Lock lock = new ReentrantLock();    public void increment() {        lock.lock();        try {            count++;        } finally {            lock.unlock();        }    }    public int getCount() {        lock.lock();        try {            return count;        } finally {            lock.unlock();        }    }}

DyQ28资讯网——每日最新资讯28at.com

(3) 原子类DyQ28资讯网——每日最新资讯28at.com

Java提供了一些原子类,如 AtomicInteger、AtomicLong 和 AtomicReference,它们通过CAS(Compare-And-Swap)操作实现了非阻塞的线程安全。DyQ28资讯网——每日最新资讯28at.com

下面的代码是AtomicInteger原子类的简单使用:DyQ28资讯网——每日最新资讯28at.com

import java.util.concurrent.atomic.AtomicInteger;public class AtomicTest {private AtomicInteger atomic = new AtomicInteger();    public void increment() {        atomic.incrementAndGet();    }    public int getCount() {        return atomic.get();    }}

DyQ28资讯网——每日最新资讯28at.com

(4) ThreadLocal 类DyQ28资讯网——每日最新资讯28at.com

ThreadLocal类提供了线程局部变量,每个线程都有自己独立的变量副本,从而避免了共享数据的竞争。DyQ28资讯网——每日最新资讯28at.com

下面的代码是ThreadLocal类的简单使用:DyQ28资讯网——每日最新资讯28at.com

public class ThreadLocalExample {private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);    public int getValue() {        return threadLocal.get();    }    public void setValue(int value) {        threadLocal.set(value);    }}

(5) 分布式锁DyQ28资讯网——每日最新资讯28at.com

Redis 分布式锁 或者 Zookeeper分布式锁是分布式环境下保证线程安全的常用方法。关于两种分布式锁的原理,会在其他的文章详细分析。DyQ28资讯网——每日最新资讯28at.com

7. 总结

线程安全是 Java多线程编程中很重要的一部分,本文讲解了什么是线程安全以及产生线程安全问题的根因,并且通过原子性,有序性,可见性对线程安全进行了分析。DyQ28资讯网——每日最新资讯28at.com

  • 硬件的多级缓存和Java与之对应的内存模型是导致线程安全的根因;
  • volatile可以保证变量的可见性,但不能保证原子性,因此无法保证线程安全;
  • synchronized,虚拟机锁,原子类,分布式锁可以保证线程的安全性;

本文链接:http://www.28at.com/showinfo-26-89393-0.html到底什么是线程安全? 如何保证线程安全?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 京东 520 晚 8 点放大招! 999 元抢戴森吹风机 + 戴森吸尘器

下一篇: ASP.NET Core 中的文件上传与下载功能实现

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • K60 Pro官方停产 第三方瞬间涨价

    虽然没有官方宣布,但Redmi的一些高管也已经透露了,Redmi K60 Pro已经停产且不会补货,这一切都是为了即将到来的K60 Ultra铺路,属于厂家的正常操作。但有意思的是该机在停产之后
  • K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    Redmi的后性能时代战略发布会今天下午如期举办,在本次发布会上,Redmi公布了多项关于和联发科的深度合作,以及新机K60 Ultra在软件和硬件方面的特性,例如:“K60 至尊版,双芯旗舰
  • 对标苹果的灵动岛 华为带来实况窗功能

    继苹果的灵动岛之后,华为也在今天正式推出了“实况窗”功能。据今天鸿蒙OS 4.0的现场演示显示,华为的实况窗可以更高效的展现出实时通知,比如锁屏上就能看到外卖、打车、银行
  • 6月安卓手机好评榜:魅族20 Pro蝉联冠军

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年6月1日至6月30日,仅限国内市场。第一名:魅族20 Pro好评率:95%5月份的时候魅族20 Pro就是
  • 企业采用CRM系统的11个好处

    客户关系管理(CRM)软件可以为企业提供很多的好处,从客户保留到提高生产力。  CRM软件用于企业收集客户互动,以改善客户体验和满意度。  CRM软件市场规模如今超过580
  • 之家push系统迭代之路

    前言在这个信息爆炸的互联网时代,能够及时准确获取信息是当今社会要解决的关键问题之一。随着之家用户体量和内容规模的不断增大,传统的靠"主动拉"获取信息的方式已不能满足用
  • 华为将推出盘古数字人大模型 可帮助用户12小时完成数字人生成

    在今日举行的2023年华为云数字文娱AI创新峰会上,华为云全球Marketing与销售服务总裁石冀琳表示,华为云将在后续推出盘古数字人大模型,可帮助用户12小
  • 苹果公司要求三星和LG Display生产「无边框」OLED iPhone显示屏

    据 The Elec 报道,苹果已要求其供应商为未来的 iPhone 型号开发「无边框」OLED 显示面板。苹果显然已要求三星和 LG Display 开发新的 OLED 显示面
Top