java 并发编程学习笔记(三)之 线程安全性

举报
小米粒-biubiubiu 发表于 2020/12/03 00:01:33 2020/12/03
【摘要】                                              线程安全性 (1)java.util.concurrent.atomic 包 AtomicInteger 、 AtomicLong 、LongAdder的使用 @Slf4j@ThreadSafepublic class AtomicExample2 { //请求次数 pri...

                                             线程安全性

(1)java.util.concurrent.atomic

  • AtomicInteger 、 AtomicLong 、LongAdder的使用

  
  1. @Slf4j
  2. @ThreadSafe
  3. public class AtomicExample2 {
  4. //请求次数
  5. private static int clientTotal = 5000;
  6. //允许同时运行的线程数
  7. private static int threadTotal = 200;
  8. //计数
  9. //public static AtomicInteger count = new AtomicInteger(0);
  10. //public static AtomicLong count = new AtomicLong(0);
  11. /**
  12. * 一个或多个变量一起保持初始为零long和。 当跨线程争用更新(方法add(long) )时,
  13. * 该变量集可以动态增长以减少争用。 方法sum() (或等效地, longValue() )返回保持总和的整个变量组合的当前总和。
  14. * 当多个线程更新用于诸如收集统计信息,而不是细粒度同步控制的常用总和时,此类通常优于AtomicLong 。
  15. * 在低更新争议下,这两类具有相似的特征。 但是,在高度争议的情况下,这一类的预期吞吐量明显高于牺牲更高的空间消耗。
  16. *
  17. * LongAdders可以使用ConcurrentHashMap来维护可扩展的频率映射(直方图或多集的形式)。
  18. * 例如,要将一个计数添加到ConcurrentHashMap<String,LongAdder> freqs ,如果尚未存在,
  19. * 则可以使用freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
  20. */
  21. public static LongAdder count = new LongAdder();
  22. public static void main(String[] args) {
  23. ExecutorService executorService = Executors.newCachedThreadPool();
  24. final Semaphore semaphore = new Semaphore(threadTotal);
  25. final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
  26. for (int i = 0; i < clientTotal; i++) {
  27. executorService.execute(() -> {
  28. try {
  29. semaphore.acquire();
  30. add();
  31. semaphore.release();
  32. } catch (InterruptedException e) {
  33. log.error("exception", e);
  34. }
  35. countDownLatch.countDown();
  36. });
  37. }
  38. try {
  39. countDownLatch.await();
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. executorService.shutdown();
  44. log.info("count:{}", count);
  45. }
  46. private static void add() {
  47. count.increment();
  48. }
  49. }

通过查看AtomicInteger源码,可以看到AtomicInteger以原子方式更新数值,确保了线程安全性。


  
  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. }
  9. public final int getAndAddInt(Object var1, long var2, int var4) {
  10. int var5;
  11. do {
  12. var5 = this.getIntVolatile(var1, var2);
  13. } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  14. return var5;
  15. }
  • AtomicReference 、AtomicIntegerFieldUpdater、AtomicBoolean的使用

  
  1. @Slf4j
  2. @ThreadSafe
  3. public class AtomicExample3 {
  4. @Data
  5. static class Student {
  6. private int age=0 ;
  7. public Student(int age) {
  8. this.age = age;
  9. }
  10. }
  11. //请求次数
  12. private static AtomicReference<Integer> count =new AtomicReference<Integer>(0);
  13. private static AtomicReference<Student> studentAtomic =new AtomicReference<Student>(new Student(0));
  14. public static void main(String[] args){
  15. //原子更新integer
  16. count.compareAndSet(0,2);
  17. count.compareAndSet(0,1);
  18. count.compareAndSet(1,3);
  19. count.compareAndSet(2,4);
  20. count.compareAndSet(3,5);
  21. log.info("count:{}",count.get());
  22. //原子更新学生对象的age
  23. studentAtomic.compareAndSet(studentAtomic.get(),new Student(10));
  24. log.info("count:{}",studentAtomic.get().getAge());
  25. }
  26. }

  
  1. @Slf4j
  2. @ThreadSafe
  3. public class AtomicExample4 {
  4. /**
  5. * AtomicIntegerFieldUpdater
  6. *
  7. * 基于反射的实用程序,可以对指定类的指定volatile int字段进行原子更新。 该类设计用于原子数据结构,其中同一节点的多个字段独立受原子更新的影响。
  8. * 请注意, compareAndSet中的compareAndSet方法的保证弱于其他原子类。 因为这个类不能确保该字段的所有用途都适用于原子访问的目的,所以它可以
  9. * 保证原子性仅在相同更新set上的compareAndSet和set其他调用。
  10. *
  11. * 类型为T参数的对象参数不是传递给newUpdater(java.lang.Class<U>, java.lang.String)的类的实例将导致抛出ClassCastException 。
  12. */
  13. private static AtomicIntegerFieldUpdater<AtomicExample4> updater = AtomicIntegerFieldUpdater.newUpdater(AtomicExample4.class, "count");
  14. @Getter
  15. private volatile int count = 100;
  16. public static void main(String[] args) {
  17. AtomicExample4 atomicExample4 = new AtomicExample4();
  18. if (updater.compareAndSet(atomicExample4, 100, 120)) {
  19. log.info("update success 1,{}", atomicExample4.getCount());
  20. }
  21. if (updater.compareAndSet(atomicExample4, 100, 120)) {
  22. log.info("update success 2,{}", atomicExample4.getCount());
  23. } else {
  24. log.info("update failed,{}", atomicExample4.getCount());
  25. }
  26. }
  27. }

  
  1. @Slf4j
  2. @ThreadSafe
  3. public class AtomicExample5 {
  4. //请求次数
  5. private static int clientTotal = 5000;
  6. //允许同时运行的线程数
  7. private static int threadTotal = 200;
  8. //boolean标识
  9. public static AtomicBoolean flag = new AtomicBoolean(false);
  10. public static void main(String[] args) {
  11. ExecutorService executorService = Executors.newCachedThreadPool();
  12. final Semaphore semaphore = new Semaphore(threadTotal);
  13. final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
  14. for (int i = 0; i < clientTotal; i++) {
  15. executorService.execute(() -> {
  16. try {
  17. semaphore.acquire();
  18. test();
  19. semaphore.release();
  20. } catch (InterruptedException e) {
  21. log.error("exception", e);
  22. }
  23. countDownLatch.countDown();
  24. });
  25. }
  26. try {
  27. countDownLatch.await();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. executorService.shutdown();
  32. log.info("count:{}", flag.get());
  33. }
  34. private static void test() {
  35. if (flag.compareAndSet(false, true)) {
  36. log.info("多个线程同时操作我,我也只会执行一次");
  37. }
  38. }
  39. }

(2)原子性 - 锁

synchronized : 依赖jvm

Lock :依赖特殊的cpu指令,代码实现,ReentrantLock 等

 


  
  1. @Slf4j
  2. public class SynchronizedExample1 {
  3. //修饰一个代码块,作用范围:整个代码块,只作用于调用于该方法的对象,不同的对象之间互不影响
  4. public void test1(String objectName) {
  5. synchronized (this) {
  6. for (int i = 0; i < 10; i++) {
  7. log.info("test1-" + objectName + "-{}", i);
  8. }
  9. }
  10. }
  11. //修饰一个方法,作用范围:整个方法,只作用于调用于该方法的对象,不同的对象之间互不影响
  12. public synchronized void test2(String objectName) {
  13. for (int i = 0; i < 10; i++) {
  14. log.info("test2-" + objectName + "-{}", i);
  15. }
  16. }
  17. //修饰静态方法,作用范围:整个方法,作用于所有对象,不同的对象也会相互影响,进行锁竞争
  18. public static synchronized void test3(String objectName) {
  19. for (int i = 0; i < 10; i++) {
  20. log.info("test2-" + objectName + "-{}", i);
  21. }
  22. }
  23. //修饰静态方法,作用范围:整个方法,作用于所有对象,不同的对象也会相互影响,进行锁竞争
  24. public static synchronized void test4(String objectName) {
  25. synchronized (SynchronizedExample1.class) {
  26. for (int i = 0; i < 10; i++) {
  27. log.info("test2-" + objectName + "-{}", i);
  28. }
  29. }
  30. }
  31. public static void main(String[] args) {
  32. //声明一个对象
  33. SynchronizedExample1 synchronizedExample1 = new SynchronizedExample1();
  34. ExecutorService service = Executors.newCachedThreadPool();
  35. // service.execute(() -> {
  36. // // synchronizedExample1.test1("Example1");
  37. // synchronizedExample1.test2("Example1");
  38. // });
  39. // service.execute(()->{
  40. // // synchronizedExample1.test1("Example1");
  41. // synchronizedExample1.test2("Example1");
  42. // });
  43. //声明二个对象
  44. SynchronizedExample1 synchronizedExample2 = new SynchronizedExample1();
  45. // service.execute(() -> {
  46. // // synchronizedExample1.test1("Example1");
  47. // synchronizedExample1.test2("Example1");
  48. // });
  49. // service.execute(()->{
  50. // // synchronizedExample1.test1("Example1");
  51. // synchronizedExample2.test2("Example2");
  52. // });
  53. // service.execute(() -> {
  54. // // synchronizedExample1.test1("Example1");
  55. // synchronizedExample1.test3("Example1");
  56. // });
  57. // service.execute(()->{
  58. // // synchronizedExample1.test1("Example1");
  59. // synchronizedExample2.test3("Example2");
  60. // });
  61. service.execute(() -> {
  62. // synchronizedExample1.test1("Example1");
  63. synchronizedExample1.test4("Example1");
  64. });
  65. service.execute(() -> {
  66. // synchronizedExample1.test1("Example1");
  67. synchronizedExample2.test4("Example2");
  68. });
  69. }
  70. }

 

 

  •  可见性

 

volatile

 


  
  1. @Slf4j
  2. @NotThreadSafe
  3. public class ConcurrencyTest {
  4. //请求次数
  5. private static int clientTotal = 5000;
  6. //允许同时运行的线程数
  7. private static int threadTotal = 200;
  8. //计数
  9. public static volatile int count = 0;
  10. public static void main(String[] args) {
  11. ExecutorService executorService = Executors.newCachedThreadPool();
  12. final Semaphore semaphore = new Semaphore(threadTotal);
  13. final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
  14. for (int i = 0; i < clientTotal; i++) {
  15. executorService.execute(() -> {
  16. try {
  17. semaphore.acquire();
  18. add();
  19. semaphore.release();
  20. } catch (InterruptedException e) {
  21. log.error("exception", e);
  22. }
  23. countDownLatch.countDown();
  24. });
  25. }
  26. try {
  27. countDownLatch.await();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. executorService.shutdown();
  32. log.info("count:{}", count);
  33. }
  34. private static void add() {
  35. count++;
  36. /**
  37. * count++进行了 三步操作
  38. * 读取 count
  39. * count + =1
  40. * 写入 count
  41. * 当我们没有用volatile 修饰时,无法确保每个线程读取到的值是最新的,就会出问题。
  42. *
  43. * 当我们用了volatile 修饰之后,虽然可以确保每个线程读取的值是最新的,但是不同线程进行+1 操作时还是会出问题。
  44. * 所以 volatile 不具备原子性,因此volatile 特别适合用于状态标记变量,
  45. * 当其中一个线程 修改了boolean值之后,可以在另外一个线程中,读取到最新的 boolean变量值
  46. */
  47. }

 例如:

 

 

(3)happens-before原则

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作,先行发生于书写在后面的操作

锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作

volatile变量:对于一个变量的写操作先行发生于后面对这个变量的读操作

 传递规则:如果操作A先行发生与操作B,而操作B又先行发生于操作C,那么操作A先行发生于操作C。

线程启动规则: Thread对象的start()方法先行发生于此线程的每一个动作

线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生

线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束,

Thread.isAlive()的返回值手段检测到线程已经终止执行

对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

文章来源: blog.csdn.net,作者:血煞风雨城2018,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_31905135/article/details/84189111

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。