Java并发之 CAS + Thread.join / CountDownLatch 方式实现线程安全
来源:互联网 发布:向日葵软件如何视频 编辑:程序博客网 时间:2024/06/04 22:36
一、问题描述
在多线程开发中,由并发引起的问题很不容易发觉,这里分别实现了线程安全和非线程安全的两种计数器。可以明显的看出多线程并发引发的数据丢失问题。
二、问题分析
- 这里非线程安全的计数器的起因是计数器中的
count++;
操作是非原子操作。 为解决
count++;
非原子操作问题,这里模拟了硬件级解决方案CAS(Compare And Swap,比较并交换),是一种乐观锁方案。CAS有3个操作数,内存位置V,旧的预期值A和新值B。CAS的意思为:我认为V的值应该是A,如果是,那么将其赋值为B,若不是,则不修改,并告诉我应该为多少。它抱着成功的希望进行更新,并且如果另一个线程在上次检查后更新了该变量,它能够发现错误。
CAS模拟代码:
public synchronized int increaseCountWithCas(int exceptValue, int newValue){ int oldValue = count; if(oldValue == exceptValue){ count = newValue; } return oldValue; }
- 在模拟多线程并发时,利用两种方式实现线程:
- 扩展Thread类
- 使用runnable
三、代码结构
四、counter代码
非线程安全计数器
package counter;/*** * 非线程线程安全的计数器 * @author zq * */public class UnsafeCounter { private ValueWithoutCas valueWithoutCas = new ValueWithoutCas(); public int getValue(){ return valueWithoutCas.getCount(); } public int increase(){ valueWithoutCas.increaseCount(); return valueWithoutCas.getCount(); }}
其中,ValueWithoutCas代码如下
package counter;/*** * 非线程安全计数器使用的计数类 * @author zq * */public class ValueWithoutCas { private int count; //不安全的计数值增加 public void increaseCount(){ count++; } public int getCount(){ return count; }}
线程安全计数器
package counter;/*** * 基于CAS实现的非阻塞线程安全计数器 * @author zq * */public class SafeCounter { private ValueWithCas valueWithCas = new ValueWithCas(); public int getValue(){ return valueWithCas.getCount(); } public int increase(){ int v; do{ v = valueWithCas.getCount(); }while(v != valueWithCas.increaseCountWithCas(v, v + 1)); return v + 1; }}
其中,valueWithCas 代码如下
package counter;/*** * 线程安全计数器使用的计数类 * @author zq * */public class ValueWithCas { private int count; //模拟CAS实现计数值增加 public synchronized int increaseCountWithCas(int exceptValue, int newValue){ int oldValue = count; if(oldValue == exceptValue){ count = newValue; } return oldValue; } public synchronized int getCount(){ return count; }}
五、runnable方式模拟线程
操作非线程安全计数器
package runnablePackage;import counter.UnsafeCounter;/*** * 模拟线程:操作非线程安全计数器 * @author zq */public class RunnableWithoutCAS implements Runnable { private static UnsafeCounter unsafeCounter = new UnsafeCounter(); @Override public void run() { unsafeCounter.increase(); } public static UnsafeCounter getUnsafeCounter(){ return unsafeCounter; }}
操作线程安全计数器
package runnablePackage;import counter.SafeCounter;/*** * 模拟线程:操作线程安全计数器 * @author zq */public class RunnableWithCAS implements Runnable { private static SafeCounter safeCounter = new SafeCounter(); @Override public void run() { safeCounter.increase(); } public static SafeCounter getSafeCounter(){ return safeCounter; }}
六、thread方式模拟线程
操作非线程安全计数器
package threadPackage;import counter.UnsafeCounter;/*** * 模拟线程:操作非线程安全计数器 * @author zq */public class ThreadIncWithUnsafe extends Thread { UnsafeCounter unsafeCounter; public ThreadIncWithUnsafe(UnsafeCounter unsafeConunter){ super(); this.unsafeCounter = unsafeConunter; } public void run(){ unsafeCounter.increase(); }}
操作线程安全计数器
package threadPackage;import counter.SafeCounter;/*** * 模拟线程:操作线程安全计数器 * @author zq */public class ThreadIncWithSafe extends Thread{ SafeCounter safeCounter; public ThreadIncWithSafe(SafeCounter safeCounter){ super(); this.safeCounter = safeCounter; } public void run(){ safeCounter.increase(); }}
七、测试代码
以runnable和thread方式分别模拟10000个线程操作线程安全计数器和非线程安全计数器(计数器初始值为0)。正常情况下,10000个线程操作完成后,计数器值应该为10000。
这里使用
Thread.currentThread().join(10)
方法实现main线程等待它启动的所有子进程完成后输出计数器结果。package cas; import counter.SafeCounter; import counter.UnsafeCounter; import runnablePackage.RunnableWithCAS; import runnablePackage.RunnableWithoutCAS; import threadPackage.ThreadIncWithSafe; import threadPackage.ThreadIncWithUnsafe; public class CasTest { public static void main(String[] args) { doWithThread(); System.out.println(); doWithRunnable(); } public static void doWithThread(){ //操作非线程安全计数器 UnsafeCounter unsafeCounter = new UnsafeCounter(); for(int i = 1; i <= 10000; i++){ Thread t = new ThreadIncWithUnsafe(unsafeCounter); t.start(); } waitSubThreadComplete(); System.out.println("doWithThread, unsafe result: " + unsafeCounter.getValue()); //操作线程安全计数器 SafeCounter safeCounter = new SafeCounter(); for(int j = 1; j <= 10000; j++){ Thread T = new ThreadIncWithSafe(safeCounter); T.start(); } waitSubThreadComplete(); System.out.println("doWithThread, safe result: " + safeCounter.getValue()); } public static void doWithRunnable(){ //操作非线程安全计数器 for(int i = 1; i <= 10000; i++){ Thread t = new Thread(new RunnableWithoutCAS()); t.start(); } waitSubThreadComplete();//使main线程等待它启动的所有子进程完成后,打印计数器结果 System.out.println("doWithRunnable,unsafe result: " + RunnableWithoutCAS.getUnsafeCounter().getValue()); //操作线程安全计数器 for(int i = 1; i <= 10000; i++){ Thread t = new Thread(new RunnableWithCAS()); t.start(); } waitSubThreadComplete(); System.out.println("doWithRunnable,safe result: " + RunnableWithCAS.getSafeCounter().getValue()); } /*** * 使main线程等待它启动的所有子进程完成 */ public static void waitSubThreadComplete(){ try { Thread.currentThread().join(10); } catch (InterruptedException e) { e.printStackTrace(); } } }
在上述测试代码中,doWithRunnable函数也可以使用CountDownLatch保证main线程等待它启动的所有子进程完成后再输出计数器的值。代码如下:
/** * 使用CountDownLatch保证所有子进程完成后再输出结果 * */ public static void doWithRunnable(){ //操作非线程安全计数器 final CountDownLatch unsafeEndGate = new CountDownLatch(10000); for(int i = 1; i <= 10000; i++){ Thread t = new Thread(){ public void run(){ try{ try{ new RunnableWithoutCAS().run(); }finally{ unsafeEndGate.countDown(); } }catch(Exception e){ } } }; t.start(); }// waitSubThreadComplete();//使main线程等待它启动的所有子进程完成后,打印计数器结果 try { unsafeEndGate.await(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("doWithRunnable,unsafe result: " + RunnableWithoutCAS.getUnsafeCounter().getValue()); //操作线程安全计数器 final CountDownLatch safeEndGate = new CountDownLatch(10000); for(int i = 1; i <= 10000; i++){ Thread t = new Thread(){ public void run(){ try{ try{ new RunnableWithCAS().run(); }finally{ safeEndGate.countDown(); } }catch(Exception e){ } } }; t.start(); }// waitSubThreadComplete(); try { safeEndGate.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doWithRunnable,safe result: " + RunnableWithCAS.getSafeCounter().getValue()); }
八、运行结果
doWithThread, unsafe result: 9998doWithThread, safe result: 10000doWithRunnable,unsafe result: 9997doWithRunnable,safe result: 10000
0 0
- Java并发之 CAS + Thread.join / CountDownLatch 方式实现线程安全
- 【Java并发】(二) 线程同步之Thread.join()、CountDownLatch、CyclicBarrier
- 采用Thread.join()或CountDownLatch来实现线程间同步
- JAVA线程并发性之CAS算法,模拟实现代码
- Java中的线程Thread方法之---join()
- Java中的线程Thread方法之---join()
- 【Java并发编程】Thread方式创建线程
- Java并发之CountDownLatch
- Java并发之CountDownLatch
- Java 并发之线程安全
- Java并发编程之CountDownLatch,CyclicBarrier实现一组线程相互等待、唤醒
- 【5】Java并发编程:线程同步工具之CountDownLatch类
- 《java并发编程实战》:线程同步辅助类之CountDownLatch
- Java线程之CountDownLatch
- java线程之CountDownLatch
- java并发之CAS
- java-Thread线程安全
- Java Thread 之 Join
- viewFlipper的学习使用,可做引导页
- 构建C1000K的服务器(2) – 实现百万连接的comet服务器
- 只有 DBA 才能导入由其他 DBA 导出的文件
- 安卓开发学习资源:控件+图标
- C++的静态库与动态库
- Java并发之 CAS + Thread.join / CountDownLatch 方式实现线程安全
- 判断一个数组是否是某个二叉树搜索树的后序遍历
- 码农小汪-spring框架学习之4-spring Bean的生命周期 ApplicationContextAware和BeanNameAware Spring Bean 的继承
- 十几个数相加
- P2P架构
- 初学python(对比java语言不同) 第六篇
- 基础教学——如何用maven工具创建一个springmvc+mybatis的项目
- 如何揪出java程序占用cpu过高的线程代码
- maven