Java并发编程之线程安全性

 更新时间:2022年04月07日 14:31:46   作者:万猫学社  
这篇文章主要介绍了Java并发编程之线程安全性,文章基于Java的相关内容详细的展开详细介绍,需要的小伙伴可以参考一下

1.什么是线程安全性

当多个线程访问某个类时,不管运行时环境采用何种调用方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

无状态的对象一定是线程安全的,比如:Servlet

2.原子性

2.1 竞争条件

由于不恰当的执行时序而出现不正确的结果的情况,就是竞争条件。

“先检查后执行”操作,即通过一个可能实效的观测结果来决定下一步的动作。比如:延迟初始化。

if(instance == null) {
    instance = new SomeObject();
}

“读取-修改-写入”的操作,其结果状态依赖于之前的状态。如:递增运算。

long count = 0;
count++;

2.2 复合操作

原子操作是指,对于访问同一个状态的所有操作(包括此操作本身)来说,这个操作是以一个原子方式执行(不可分割)的操作。

为了确保线程安全性,包含了一组必须以原子方式执行的操作,称为复合操作。

递增运算可以使用一个现有的线程安全类,确保线程安全性。如:

AtomicLong count = new AtomicLong(0);
count.incrementAndGet();

3.加锁机制

当类只有一个状态变量时,可以通过线程安全的状态变量来维护类的线程安全性。但如果类有更多的状态时,就不能只添加更多线程安全的状态变量了。要保持状态的一致性,就需要在单个原子操作中更新所以相关的状态变量。

3.1 内置锁

Java提供一种内置锁:同步代码块,它包括:一个作为锁的对象引用、一个作为由这个锁保护的代码块。

以关键字synchronized来修饰的方法就是一种横跨整个方法体的同步代码块,其中该同步代码块的锁就是方法调用所在的对象。静态的synchronized方法以Class对象作为锁。

线程在进入同步代码块之前会自动获得锁,在退出同步代码块是自动释放锁。最多只有一个线程能持有这种锁,因此同步代码会以原子方式执行。

3.2 重入

内置锁是可重入的,意味着获取锁的操作的粒度是线程,不是调用。当某个线程试图获得一个已经由它自己持有的锁时,这个请求也会成功。

重入进一步提升了加锁行为的封装性,简化了面向对象并发代码的开发。

public class Widget {
    public synchronized void doSomething() {
        //......
    }
}
public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        //......
        super.doSomething();//假如没有可重入的锁,该语句将产生死锁。
    }
}

4.用锁保护状态

对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,在这种情况下,称状态变量是由这个锁保护的。

5.活跃性与性能

粗粒度地使用锁,虽然确保了线程安全性,但可能造成性能问题和活跃度问题,如:

@ThreadSafe
public class SynchronizedFactorizer implements Servlet {
    @GuardedBy("this") private BigInteger lastNumber;
    @GuardedBy("this") private BigInteger[] lastFactors;

    public synchronized void service(ServletRequest req,
                                     ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        if (i.equals(lastNumber))
            encodeIntoResponse(resp, lastFactors);
        else {
            BigInteger[] factors = factor(i);//因数分解计算
            lastNumber = i;
            lastFactors = factors;//存放上一次计算结果
            encodeIntoResponse(resp, factors);
        }
    }
}

可以通过缩小同步代码块,既确保servlet的并发型,又维护线程安全性。不要把本应是原子操作拆分到多个同步代码块中,尽量将不影响共享状态且执行时间较长的操作从同步代码中分离出来。如:

public class CachedFactorizer implements Servlet {
    @GuardedBy("this") private BigInteger lastNumber;
    @GuardedBy("this") private BigInteger[] lastFactors;

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req); 
        BigInteger[] factors = null;      
        synchronized (this) {
            if (i.equals(lastNumber)) {
               factors = lastFactors.clone();
            }         
        }    
        if (factors == null) {        
            factors = factor(i);
            synchronized (this) {
               lastNumber = i;
               lastFactors = factors.clone();
            }
        }
        encodeIntoResponse(resp, factors);
    }
}

到此这篇关于Java并发编程之线程安全性的文章就介绍到这了,更多相关Java 线程安全性内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • @Scheduled fixedDelayString 加载properties配置方式

    @Scheduled fixedDelayString 加载properties配置方式

    这篇文章主要介绍了@Scheduled fixedDelayString 加载properties配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • java long 类型数据的赋值方式

    java long 类型数据的赋值方式

    这篇文章主要介绍了java long 类型数据的赋值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Spring使用IOC与DI实现完全注解开发

    Spring使用IOC与DI实现完全注解开发

    IOC也是Spring的核心之一了,之前学的时候是采用xml配置文件的方式去实现的,后来其中也多少穿插了几个注解,但是没有说完全采用注解实现。那么这篇文章就和大家分享一下,全部采用注解来实现IOC + DI
    2022-09-09
  • java常用的加密解决方案分享

    java常用的加密解决方案分享

    这篇文章全面介绍了Java中加解密技术的应用,包括哈希函数、对称加密、非对称加密、消息认证码和数字签名等,它详细解释了每种技术的工作原理,并提供了相应的Java代码示例,文章还强调了密钥管理的重要性,并提出了在实际应用中遵循的最佳实践
    2025-01-01
  • java集合模拟实现斗地主洗牌和发牌

    java集合模拟实现斗地主洗牌和发牌

    这篇文章主要为大家详细介绍了java集合模拟实现斗地主洗牌和发牌,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Spring在多线程环境下如何确保事务一致性问题详解

    Spring在多线程环境下如何确保事务一致性问题详解

    这篇文章主要介绍了Spring在多线程环境下如何确保事务一致性问题详解,说到异步执行,很多小伙伴首先想到Spring中提供的@Async注解,但是Spring提供的异步执行任务能力并不足以解决我们当前的需求,需要的朋友可以参考下
    2023-11-11
  • 使用Spring Security集成手机验证码登录功能实现

    使用Spring Security集成手机验证码登录功能实现

    本文详细介绍了如何利用SpringSecurity来实现手机验证码的注册和登录功能,在登录过程中,同样需通过验证码进行验证,文章还提供了相关的代码实现
    2024-10-10
  • Java中递归、循环的优劣分析

    Java中递归、循环的优劣分析

    这篇文章主要给大家介绍了关于Java中递归、循环的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-03-03
  • SpringBoot使用注解进行分页的实现示例

    SpringBoot使用注解进行分页的实现示例

    分页使用可以说非常普遍了,有时候会需要非常灵活的方式去开启或关闭分页,尝试使用一下注解的方式来进行分页,本文主要介绍了SpringBoot使用注解进行分页的实现示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Zookeeper中如何解决zookeeper.out文件输出位置问题

    Zookeeper中如何解决zookeeper.out文件输出位置问题

    这篇文章主要介绍了Zookeeper中如何解决zookeeper.out文件输出位置问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04

最新评论