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

package com.cn.test.thread;

public class VolatileAutomic extends Thread{
private volatile static int count = 0;
public static void add() {
for (int i = 0; i < 10000; i++) {
count ++;
}
} @Override
public void run() {
add();
} public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new VolatileAutomic().start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
// System.out.println(atomicCount.get());
System.out.println("count=" + count);
}
}

运行结果:

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

package com.cn.test.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileAutomic extends Thread{
static AtomicInteger atomicCount = new AtomicInteger(0);
public static void add() {
for (int i = 0; i < 10000; i++) {
atomicCount.incrementAndGet();
}
} @Override
public void run() {
add();
} public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new VolatileAutomic().start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(atomicCount.get());
}
}

运行结果:

100000

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

AtomicInteger源码理解:

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

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

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

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

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

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

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

package com.cn.test.thread;

import java.util.concurrent.atomic.AtomicInteger;

/**
* 设计4个线程,其中两个线程 每次对j增加1,另外两个线程对j每次减少1。写出程序。
*
*/
public class TestAtomicThread extends Thread {
private static int j = 10;
static AtomicInteger atomicInteger = new AtomicInteger(j);
public TestAtomicThread(String string) {
super(string);
} @Override
public void run() {
System.out.println(Thread.currentThread().getName());
switch(Thread.currentThread().getName()) {
case "thread-1":
case "threa-2":
atomicInteger.incrementAndGet();
// System.out.println("当前线程名称:" + Thread.currentThread().getName() + ":" + atomicInteger.get());
break;
case "thread-3":
case "thread-4": atomicInteger.decrementAndGet();
// System.out.println("当前线程名称:" + Thread.currentThread().getName() + ":" + atomicInteger.get());
break;
default : break;
}
} public static void main(String[] args) {
TestAtomicThread t1 = new TestAtomicThread("thread-1");
TestAtomicThread t2 = new TestAtomicThread("thread-2");
TestAtomicThread t3 = new TestAtomicThread("thread-3");
TestAtomicThread t4 = new TestAtomicThread("thread-4");
t1.start();
t2.start();
t3.start();
t4.start();
if (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println("j====" + atomicInteger.get()); } }

最新文章

  1. EF DbContext 并发执行时可能出现的问题
  2. stanford Protege 4.3 ERROR: Bundle org.protege.common 解决方法
  3. Java设计模式之策略模式(一)
  4. 国外流行的共享网站实现:facebook,twitter,google+1,tumblr等待
  5. java导出和读取excel数据
  6. java工程师联通XX面试题目
  7. javascript中new操作符
  8. MySQL查询语句报错 sql_mode=only_full_group_by 问题
  9. 开启vue源码的解析攻略---认识flow
  10. JAVA期末考试整理
  11. Git冲突和解决冲突-测试方法
  12. mysql 高可用架构
  13. Gibs抽样
  14. SpringBoot日记——缓存的使用
  15. Install-Package:QRCoder已拥有为System.Drawing.Common定义的依赖项
  16. Cognos报表展示图片小技巧
  17. Mac OS 10.12使用SecureCRT 8.1.4无法保存密码的问题解决
  18. 【转】Excel-VBA操作文件四大方法之三
  19. 洛谷P2398 GCD SUM
  20. socket中close发生的事情,RST,pipe信号错误

热门文章

  1. C++小总结
  2. 一款不错的jQuery分页插件--pagination
  3. jmeter性能测试指标
  4. PHP中SimpleXMLElement对象字符编码
  5. P4173 残缺的字符串
  6. 启用mysql日志记录执行过的sql
  7. 利用 Nginx 做反向代理解决微信小程序业务域名限制问题
  8. git 换行符替换
  9. Android中直接打开高德APP进行导航
  10. 如何通过SQL命令查看数据库的文件大小[转]