现有业务场景需要做一个线程间的全局变量,并且实现自增效果。

初始使用了volatile 来保证count的安全性,如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; public class TestThredd { private volatile int count = 0; public void increment() {
count++;
} private int getCount() {
return count;
} /**
* 这里模拟一个递增的任务,递增目标为50000
*/
public static void main(String[] args) throws InterruptedException {
final TestThredd counter = new TestThredd();
int workCount = 50000;
ExecutorService executor = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < workCount; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
counter.increment();
}
};
executor.execute(runnable);
}
// 关闭启动线程,执行未完成的任务
executor.shutdown();
// 等待所有线程完成任务,完成后才继续执行下一步
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
System.out.println("执行结果:count=" + counter.getCount());
}
}

执行结果

它的结果不是我们预料的50000 .通常我们需要加上在count++时 加上synchronized关键字,保证他的正确性。

如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; public class TestThredd { private volatile int count = 0; /**
* 为了保证数据的准确性,多线程的情况下需要加上synchronized关键字
* 否则会出现出乎预料的结果 这也是线程安全的重要体现
*/
public synchronized void increment() {
count++;
} private int getCount() {
return count;
} /**
* 这里模拟一个递增的任务,递增目标为50000
*/
public static void main(String[] args) throws InterruptedException {
final TestThredd counter = new TestThredd();
int workCount = 50000;
ExecutorService executor = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < workCount; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
counter.increment();
}
};
executor.execute(runnable);
}
// 关闭启动线程,执行未完成的任务
executor.shutdown();
// 等待所有线程完成任务,完成后才继续执行下一步
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
System.out.println("执行结果:count=" + counter.getCount());
}
}

为了保证数据的准确性,多线程的情况下需要加上synchronized关键字,否则会出现不安全的操作

如果我们换个方式,用AtomicInteger来替换count++,怎么做呢?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); // 使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
public void increment() {
//获取当前的值并自增
count.incrementAndGet();
}
/**
* 获取当前的值
* @return
*/
public int getCount() {
return count.get();
}
//递减
public void deIncrement(){
count.decrementAndGet();
} /**
* 这里模拟一个递增的任务,递增目标为50000
*/
public static void main(String[] args) throws InterruptedException {
final AtomicCounter counter = new AtomicCounter();
int workCount = 50000;
ExecutorService executor = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < workCount; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
counter.increment();
}
};
executor.execute(runnable);
}
// 关闭启动线程,执行未完成的任务
executor.shutdown();
// 等待所有线程完成任务,完成后才继续执行下一步
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
System.out.println("执行结果:count=" + counter.getCount());
}
}

AtomicInteger很轻松的实现了线程安全的变量操作

Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe实现的包装类。

使用原子的方式更新基本类型,Atomic包提供了以下3个类。
AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新长整型。

通过原子的方式更新数组里的某个元素,Atomic包提供了以3类
AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。

AtomicInteger是在使用非阻塞算法实现并发控制,在一些高并发程序中非常适合,但并不能每一种场景都适合,不同场景要使用使用不同的数值类。

这是由硬件提供原子操作指令实现的,这里面用到了一种并发技术:CAS。在非激烈竞争的情况下,开销更小,速度更快。

参考:

http://blog.csdn.net/sunxianghuang/article/details/52277370

http://blog.csdn.net/u012734441/article/details/51619751

http://blog.csdn.net/jan_s/article/details/47025095

AtomicInteger保证线程安全的全局变量的更多相关文章

  1. 最近面试被问到一个问题,AtomicInteger如何保证线程安全?

    最近面试被问到一个问题,AtomicInteger如何保证线程安全?我查阅了资料 发现还可以引申到 乐观锁/悲观锁的概念,觉得值得一记. 众所周知,JDK提供了AtomicInteger保证对数字的操 ...

  2. AtomicInteger如何保证线程安全以及乐观锁/悲观锁的概念

    众所周知,JDK提供了AtomicInteger保证对数字的操作是线程安全的,线程安全我首先想到了synchronized和Lock,但是这种方式又有一个名字,叫做互斥锁,一次只能有一个持有锁的线程进 ...

  3. 多线程下C#如何保证线程安全?

    多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是 ...

  4. ASP.NET MVC Filters 4种默认过滤器的使用【附示例】 数据库常见死锁原因及处理 .NET源码中的链表 多线程下C#如何保证线程安全? .net实现支付宝在线支付 彻头彻尾理解单例模式与多线程 App.Config详解及读写操作 判断客户端是iOS还是Android,判断是不是在微信浏览器打开

    ASP.NET MVC Filters 4种默认过滤器的使用[附示例]   过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响 ...

  5. C#多线程下如何保证线程安全?

    多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是 ...

  6. Java中如何保证线程顺序执行

    只要了解过多线程,我们就知道线程开始的顺序跟执行的顺序是不一样的.如果只是创建三个线程然后执行,最后的执行顺序是不可预期的.这是因为在创建完线程之后,线程执行的开始时间取决于CPU何时分配时间片,线程 ...

  7. 解析ThreadPoolExecutor类是如何保证线程池正确运行的

    摘要:对于线程池的核心类ThreadPoolExecutor来说,有哪些重要的属性和内部类为线程池的正确运行提供重要的保障呢? 本文分享自华为云社区<[高并发]通过源码深度解析ThreadPoo ...

  8. EF 保证线程内唯一 上下文的创建

    1.ef添加完这个对象,就会自动返回这个对象数据库的内容,比如下面这个表是自增ID 最后打印出来的ID  就是自增的结果 2.lambda 中怎么select * var userInfoList = ...

  9. 在JAVA中ArrayList如何保证线程安全

    [b]保证线程安全的三种方法:[/b]不要跨线程访问共享变量使共享变量是final类型的将共享变量的操作加上同步一开始就将类设计成线程安全的, 比在后期重新修复它,更容易.编写多线程程序, 首先保证它 ...

随机推荐

  1. 关于Assembly.LoadFrom和Assembly.LoadFile的区别

    区别: 1.Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("a.dll"),则载入a.dll,假如a.dll中引用了b.dll的 ...

  2. 7.12归来赛_B

    Prime Judge 时间限制 1000 ms 内存限制 65536 KB 题目描写叙述 众所周知.假设一个正整数仅仅能被1和自身整除,那么该数被称为素数.题目的任务非常easy.就是判定一个数是否 ...

  3. linux系统常用命令 -设置文件夹读写权限

    设置文件夹的读写权限: sudo chmod -R 777 /data 权限码描述 sudo chmod 600 ××× (只有所有者有读和写的权限)sudo chmod 644 ××× (所有者有读 ...

  4. CentOS添加新网卡network-scripts目录下找不到网卡配置文件

    问题描述: 使用VMware Workstation虚拟机,安装好CentOS7虚拟机后(原本只有一张网卡ifcfg-ens33),重新添加了一个新的网卡. 进入CentOS7系统后,使用ip add ...

  5. requests.exceptions.MissingSchema: Invalid URL 'xxxxxxxxxxxxx': No schema supplied. Perhaps you meant xxxxxxxxxxxxx

    import requests session = requests.session() carProposalUrl = "www.caaaa.com.cn/aaaa/aaaaa/carP ...

  6. Openresty配置文件上传下载

    1. 下载包安装Openresty openresty-1.13.6.1下载地址 https://openresty.org/download/openresty-1.13.6.1.tar.gz 安装 ...

  7. atitit.nfc 身份证 银行卡 芯片卡 解决方案 attilax总结

    atitit.nfc 身份证 银行卡 芯片卡 解决方案 attilax总结 1. nfc(近距离无线通讯技术)  1 2. 工作模式 1 3. NFC 蓝牙 红外具体对比如下表: 2 4. TypeA ...

  8. Vivado使用技巧:封装自己设计的IP核

    概述   Vivado在设计时可以感觉到一种趋势,它鼓励用IP核的方式进行设计.“IP Integrator”提供了原理图设计的方式,只需要在其中调用设计好的IP核连线.IP核一部分来自于Xilinx ...

  9. dmesg命令应用

    昨晚上线服务的时候,看log偶然发现服务在启动半小时左右就会被supervise重新拉起,也没有core.通过重新启动的服务发现内存飙涨,且持续增加,怀疑是内存打满,进程被kill了. 其实怀疑是正确 ...

  10. 李洪强iOS开发之iOS工具收集

    李洪强iOS开发之iOS工具收集 项目 简述 日期 我是怎么慢慢变懒的 : Jenkins + 蒲公英 使用Jenkins + 蒲公英使得项目打包给测试人员自动化,大大节省了劳动力 2015.04.1 ...