validate 关键字可以保证多线程之间的可见性,但是不能保证原子操作。(需要了解java内存模型jmm)

  1. package com.cn.test.thread;
  2.  
  3. public class VolatileAutomic extends Thread{
  4. private volatile static int count = 0;
  5. public static void add() {
  6. for (int i = 0; i < 10000; i++) {
  7. count ++;
  8. }
  9. }
  10.  
  11. @Override
  12. public void run() {
  13. add();
  14. }
  15.  
  16. public static void main(String[] args) {
  17. for (int i = 0; i < 10; i++) {
  18. new VolatileAutomic().start();
  19. }
  20. while (Thread.activeCount() > 1) {
  21. Thread.yield();
  22. }
  23. // System.out.println(atomicCount.get());
  24. System.out.println("count=" + count);
  25. }
  26. }

运行结果:

  1. count=61618

上面例子中volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。

count++是一个非原子性的操作,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致的情况:

假如某个时刻变量count的值为10,线程1对变量进行自增操作,线程1先读取了变量count的原始值,然后线程1被阻塞了;线程2也对变量进行自增操作,线程2先读取了count的原始值,线程1只是进行了读取操作,没有进行写的操作,所以不会导致线程2中的本地缓存无效,因此线程2进行++操作,在把结果刷新到主存中去,此时线程1在还是读取原来的10 的值在进行++操作,所以线程1和线程2对于count=10进行两次++操作,结果都为11.。

上述问题解决方法:

1.采用add方法中加入sychnorized.,或者同步代码块

2.采用AtomicInteger

  1. package com.cn.test.thread;
  2.  
  3. import java.util.concurrent.atomic.AtomicInteger;
  4.  
  5. public class VolatileAutomic extends Thread{
  6. static AtomicInteger atomicCount = new AtomicInteger(0);
  7. public static void add() {
  8. for (int i = 0; i < 10000; i++) {
  9. atomicCount.incrementAndGet();
  10. }
  11. }
  12.  
  13. @Override
  14. public void run() {
  15. add();
  16. }
  17.  
  18. public static void main(String[] args) {
  19. for (int i = 0; i < 10; i++) {
  20. new VolatileAutomic().start();
  21. }
  22. while (Thread.activeCount() > 1) {
  23. Thread.yield();
  24. }
  25. System.out.println(atomicCount.get());
  26. }
  27. }

运行结果:

  1. 100000

AtomicInteger是java.util.concurrent.atomic并发包下面一个类。

AtomicInteger源码理解:

  1. static {
  2. try {
  3. valueOffset = unsafe.objectFieldOffset
  4. (AtomicInteger.class.getDeclaredField("value"));
  5. } catch (Exception ex) { throw new Error(ex); }
  6. }
  1. /**
  2. * Creates a new AtomicInteger with the given initial value.
  3. *
  4. * @param initialValue the initial value
  5. */
  6. public AtomicInteger(int initialValue) {
  7. value = initialValue;
  8. }

调用AtomicInteger的构造方法传入一个initialValue值,将initialValue复制给一个volatile 的value,

  1. /**
  2. * Atomically increments by one the current value.
  3. *
  4. * @return the updated value
  5. */
  6. public final int incrementAndGet() {
  7. return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
  8. }
  1. public final int getAndAddInt(Object var1, long var2, int var4) {
  2. int var5;
  3. do {
  4. var5 = this.getIntVolatile(var1, var2);
  5. } while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  6.  
  7. return var5;
  8. }

底层源码实现主要采用cas算法的思想,通过操作底层硬件的指令。cas锁是一种无锁的机制。cas的机制:v的值应该是A,如果是A则将值更新为B。当多个线程同时要更新V的值,

只有其中一个线程更新成功,其他线程更新失败,更新失败的线程并不会挂起,而是在继续进行竞争资源,对V的值进行操作。

cas锁的机制采用的是事物提交-- 失败--重试来保证原子性。

cas锁的缺点:当并发量大的时候,多线程一直进行重试,这样会导致CPU的开销很大。

  1. package com.cn.test.thread;
  2.  
  3. import java.util.concurrent.atomic.AtomicInteger;
  4.  
  5. /**
  6. * 设计4个线程,其中两个线程 每次对j增加1,另外两个线程对j每次减少1。写出程序。
  7. *
  8. */
  9. public class TestAtomicThread extends Thread {
  10. private static int j = 10;
  11. static AtomicInteger atomicInteger = new AtomicInteger(j);
  12. public TestAtomicThread(String string) {
  13. super(string);
  14. }
  15.  
  16. @Override
  17. public void run() {
  18. System.out.println(Thread.currentThread().getName());
  19. switch(Thread.currentThread().getName()) {
  20. case "thread-1":
  21. case "threa-2":
  22. atomicInteger.incrementAndGet();
  23. // System.out.println("当前线程名称:" + Thread.currentThread().getName() + ":" + atomicInteger.get());
  24. break;
  25. case "thread-3":
  26. case "thread-4": atomicInteger.decrementAndGet();
  27. // System.out.println("当前线程名称:" + Thread.currentThread().getName() + ":" + atomicInteger.get());
  28. break;
  29. default : break;
  30. }
  31. }
  32.  
  33. public static void main(String[] args) {
  34. TestAtomicThread t1 = new TestAtomicThread("thread-1");
  35. TestAtomicThread t2 = new TestAtomicThread("thread-2");
  36. TestAtomicThread t3 = new TestAtomicThread("thread-3");
  37. TestAtomicThread t4 = new TestAtomicThread("thread-4");
  38. t1.start();
  39. t2.start();
  40. t3.start();
  41. t4.start();
  42. if (Thread.activeCount() > 1) {
  43. Thread.yield();
  44. }
  45. System.out.println("j====" + atomicInteger.get());
  46.  
  47. }
  48.  
  49. }

AtomicInteger关键字的更多相关文章

  1. 作为一个新手的Oracle(DBA)学习笔记【转】

    一.Oracle的使用 1).启动 *DQL:数据查询语言 *DML:数据操作语言 *DDL:数据定义语言 DCL:数据控制语言 TPL:事务处理语言 CCL:指针控制语言 1.登录 Win+R—cm ...

  2. java IO操作和计算操作:工作内存和主内存 volatile关键字作用;原子操作对象AtomicInteger ....

    应该停止但无法停止的计算线程 如下线程示例,线程实例中while循环中的条件,在主线程中通过调用实例方法更新后,while循环并没有更新判断变量是否还成立.而是陷入了while(true)死循环. i ...

  3. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  4. java中关键字volatile的作用

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...

  5. 【转】Java并发编程:volatile关键字解析

    转自:http://www.importnew.com/18126.html#comment-487304 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备 ...

  6. 架构师养成记--4.volatile关键字

    volatile修饰的变量可在多个线程间可见. 如下代码,在子线程运行期间主线程修改属性值并不对子线程产生影响,原因是子线程有自己独立的内存空间,其中有主内存中的变量副本. public class ...

  7. zz剖析为什么在多核多线程程序中要慎用volatile关键字?

    [摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...

  8. Java多线程6:synchronized锁定类方法、volatile关键字及其他

    同步静态方法 synchronized还可以应用在静态方法上,如果这么写,则代表的是对当前.java文件对应的Class类加锁.看一下例子,注意一下printC()并不是一个静态方法: public ...

  9. java并发:线程同步机制之Volatile关键字&原子操作Atomic

    volatile关键字 volatile是一个特殊的修饰符,只有成员变量才能使用它,与Synchronized及ReentrantLock等提供的互斥相比,Synchronized保证了Synchro ...

随机推荐

  1. “全栈2019”Java第十七章:赋值运算符和算术运算符

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  2. 移动 UX 设计:如何设计推送通知

    这个问题你一定想过,在移动用户体验设计领域中,如何设计好一条简单的推送通知. 你注意过么,每天从不同的 App 上收到的大量的推送通知与提醒,这些通知里有多少你真的有兴趣? 每天,用户对各种没用的通知 ...

  3. 感想篇:7)知其然与知其所以然,KnowHow与KnowWhy

    本章目的:探究--知其然与知其所以然,KnowHow与KnowWhy. 1.Know-How体系与代价: 100多年的汽车研发历史表明,企业只有开发过两代车以上才能逐步建立和完善Know-How体系. ...

  4. CDQZ Day6

    1DP #2题目名称 种植 计数 棋盘 树输入文件名 plant.in count.in chess.in tree.in输出文件名 plant.out count.out chess.out tre ...

  5. P4027 [NOI2007]货币兑换

    传送门 首先有一个显然的贪心,每次操作都要做到底,为了最优不会出现只卖一部分或者只买一部分的操作 所以设 $f[i]$ 表示前 $i$ 天得到的最大价值,那么对于每一个 $i$,枚举所有 $j< ...

  6. springcloud(二)-最简单的实战

    技术储备 Spring cloud并不是面向零基础开发人员,它有一定的学习曲线. 语言基础:spring cloud是一个基于Java语言的工具套件,所以学习它需要一定的Java基础.当然,sprin ...

  7. php中慎用==

    var_dump(' 123fg456'==123);var_dump('some string' == 0);var_dump(123.0 == '123d456');var_dump(0 == & ...

  8. 微信小程序,全局变量方法的使用

    方法一:app.js 内设置全局变量(如屏宽,屏高的设置) 1.app.js文件,定义全局变量 /定义全局变量 globalData:{ userInfo:null, sysInfo:null, wi ...

  9. python学习14-模块

    引入模块的方式: 1. import 模块 2. from xxx import 模块 一.collections 模块 1.Counter() counter是一个计数器,主要用来计数,计算一个字符 ...

  10. DES加密之强制更新下载分离器

    数据加密算法(Data Encryption Algorithm,DEA)是一种对称加密算法,很可能是使用最广泛的密钥系统,特别是在保护金融数据的安全中,最初开发的DEA是嵌入硬件中的.通常,自动取款 ...