【Java并发处理代码规范】之使用volatile解决多线程内存可见性问题

Java代码规范之使用volatile解决多线程内存可见性问题

目录

该条规范是什么

该规范指出在Java编程中,使用volatile关键字可以解决多线程内存不可见性问题。对于一写多读的情况,volatile可以保证变量的同步性。但是对于多写的情况,volatile无法解决线程安全问题。

为什么这么规定

以下是该规范的原因:

  1. 内存可见性问题:在多线程环境下,如果没有合适的同步机制,一个线程对共享变量的修改可能对其他线程是不可见的,即内存可见性问题。通过使用volatile关键字可以确保变量在不同线程间的可见性。
  2. 原子性问题:对于一些复合操作(如count++),单纯使用volatile关键字并不能保证线程安全。此时可以使用AtomicInteger类来保证原子性操作,或者在JDK8及以上版本中推荐使用LongAdder对象,它比AtomicLong在性能上更好(减少乐观锁的重试次数)。

多种主要用法及其代码示例

使用volatile关键字保证变量可见性

public class Example {
    private volatile int count;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

使用AtomicInteger实现线程安全的自增操作

import java.util.concurrent.atomic.AtomicInteger;

public class Example {
    private AtomicInteger count = new AtomicInteger();

    public void increment() {
        count.addAndGet(1);
    }

    public int getCount() {
        return count.get();
    }
}

使用LongAdder实现线程安全的自增操作(适用于JDK8及以上版本)

import java.util.concurrent.atomic.LongAdder;

public class Example {
    private LongAdder count = new LongAdder();

    public void increment() {
        count.increment();
    }

    public long getCount() {
        return count.longValue();
    }
}

LongAdder 原理示例

LongAdder 是 Java 中 java.util.concurrent.atomic 包下的一个原子类,它用于高效地实现多线程环境下的长整型累加操作。相比于传统的原子类如 AtomicLongLongAdder 在高并发场景下具有更好的性能。

原理

LongAdder 的原理是采用分段锁(Striped Locking)的方式,将计数器分成多个小计数器,每个线程对应一个小计数器。在进行累加操作时,不同线程会独立地更新各自的小计数器,最后再将所有小计数器的值求和,从而得到最终的累加结果。

这种方式可以减少并发竞争,降低了线程之间的争用,提升了并发性能。

示例

以下是一个使用 LongAdder 的示例代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;

public class Example {
    private static final int NUM_THREADS = 10;
    private static final int NUM_INCREMENTS = 100000;

    public static void main(String[] args) throws InterruptedException {
        LongAdder counter = new LongAdder();
        ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS);

        for (int i = 0; i < NUM_THREADS; i++) {
            executorService.execute(() -> {
                for (int j = 0; j < NUM_INCREMENTS; j++) {
                    counter.increment();
                }
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);

        System.out.println("Counter: " + counter.sum());
    }
}

在上述示例中,首先创建了一个 LongAdder 对象 counter。然后创建了一个固定大小的线程池,并提交了多个任务,每个任务会对计数器进行一定次数的自增操作。最后等待线程池中的任务执行完毕,并输出最终的累加结果。

通过使用 LongAdder,多个线程可以并发地对计数器进行自增操作,而无需竞争全局锁,从而提升了并发性能。

需要注意的是,LongAdder 在累加结果时使用了延迟初始化,因此在调用 sum() 方法时才会真正计算出所有小计数器的累加结果。

LongAdder 还提供了其他一些方法,如 increment(), decrement(), add(), sumThenReset() 等,可以根据具体需求选择适当的方法。

LongAdder 是在 Java 8 中引入的,用于解决高并发场景下原子累加操作的性能问题。它可以作为一个替代方案来改善原子长整型操作的性能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BigDataMLApplication

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

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

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

打赏作者

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

抵扣说明:

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

余额充值