假设我们的网站要统计用户人数,我们需要通过变量的自增来实现:count++; 这个操作存在线程安全问题:
最后统计的人数是少的;
count++的操作分为三步:
读取count的值
计算count+1的值
把新值存入count
假设count值为100,两个线程A和B都执行了操作1,
再同时执行操作2,A先进行操作3,这时count值为101,
B再执行操作3,之前B读取的值是100,执行完操作3后B
的结果还是101,这样数据出现了问题。因为上面的操作
不是原子的,可以分开执行。
AtomicInteger出现解决了上面的问题,使用它来执行统计:
static AtomicInteger at = new AtomicInteger(0);
public static void main(String[] args) {
for(int i = 0;i < 10000;i++){
new Thread(()->{at.incrementAndGet();}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count = " + at.get());
}
AtomicInteger 源码里面有关键字volatile; 它确保对volatile字段的更新以可预见的方式告知其他的线程。简而言之volatile 的作用是当一个线程修改了变量时,另一个线程可以读取到这个修改后的值。
AtomicInteger能够实现整型数据的原子操作,在多线程并发的环境下能保证数据安全,而且内部使用乐观锁实现,比使用锁机制的并发性能高;
volatile保证了一个线程修改数据时,其它线程也能看到数据的修改 CAS操作保证了数据修改的安全性
多个线程执行一段逻辑,统计有多少个线程失败了
ConcurrentMap<Integer, AtomicInteger> jobTimeoutCountMap = new ConcurrentHashMap<>();
//分10个线程,每个线程自增2000次
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 2000; i++) {
AtomicInteger timeoutCount = jobTimeoutCountMap.putIfAbsent(22, new AtomicInteger(1));
if (timeoutCount != null) {
// 记录慢任务慢的次数
timeoutCount.incrementAndGet();
}
}
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(jobTimeoutCountMap.get(22));