当前位置:   article > 正文

并发之美_并发编程艺术之美

并发编程艺术之美

在我们日常生活中,事情应该一件一件的做,但是我们会感觉这样不仅效率低下,而且还特别累人,我们何不转换思想,看下图:

我们可以在烧开水的时候,顺便把洗茶具、拿茶叶的事情做了,这样是不是节省了好多的时间,所以并发顾名思义就是好多的线程一起在运行,那线程相关的知识,如果大家忘了,请看(线程详解)!!!

好了,言归正传,那什么是并发编程呢?

并行和并发

并行 :在同一个时间节点上,同时发生(是真正意义上的同时执行)

并发 :在一段时间内,对个事情交替执行

并发编程:在某些特定的场景中,有大量的请求访问同一个资源,会出现线程安全的问题,所以需要通过编程来控制解决让多个线程依次访问资源,称为并发。

并发的根本原因

以前单核CPU时,只能支持单线程模式,现在多核CPU,我们自然而然的可以支持多线程模式,在此之前,我们先浅谈下JMM(java内存模型)

java内存模型:它是java虚拟机规范的一种工作模式,将内存分为主内存和工作内存,变量数据存储在主内存中,线程在操作变量的时候会将主内存中的数据复制一份到工作内存,然后在工作内存中操作完后,再将数据写回主内存中。

多线程的出现,随之而来的也是与之对应的问题,下面我会为大家一一讲解!!!

在比赛的两头,中间隔着巨高的墙体,两位选手都风快的向终点赶去,但是墙体之隔,使他们不知道对方的存在。

可见性:多个线程分别同时对共享数据操作,彼此之间不可见,操作完写回主内存,有可能出现问题

有序性:为了性能,虚拟机对一些代码指令的执行顺序进行重排,以提高速度

  1. /*
  2. 模拟指令重排序
  3. */
  4. public class Reorder {
  5. private static int x;
  6. private static int y;
  7. private static int a;
  8. private static int b;
  9. public static void main(String[] args) throws InterruptedException {
  10. int i = 0;
  11. for(;;) {
  12. i++;
  13. x = 0; y = 0;
  14. a = 0; b = 0;
  15. Thread one = new Thread(new Runnable() {
  16. public void run() {
  17. a = 1;
  18. x = b;
  19. }
  20. });
  21. Thread other = new Thread(new Runnable() {
  22. public void run() {
  23. b = 1;
  24. y = a;
  25. }
  26. });
  27. one.start();
  28. other.start();
  29. one.join();
  30. other.join();
  31. String result = "第" + i + "次 (" + x + "," + y + ")";
  32. if(x == 0 && y == 0) {
  33. System.err.println(result);
  34. break;
  35. } else {
  36. System.out.println(result);
  37. }
  38. }
  39. }
  40. }

在运行的过程中,系统先执行了后面的代码以至于跳出循环,这就是多线程出现的第二个问题无序性

原子性:一个或多个操作在 CPU 执行的过程中不被中断的特性

两次count++,我们应该得到的是count=2,但是结果确实count=1,这就是多线程出现的问题之一(不可见性)

原因

  • 缓存(工作内存)带来了不可见性

  • 指令重排优化带来了无序性

  • 线程切换带来了非原子性

不可见变可见,无序变有序

让我们接下来介绍一个关键字volatile

底层实现原理:在底层指令级别进行控制,volatile修饰的变量在操作前,添加内存屏障,不让它的指令干扰,volatile修饰的变量添加内存屏障之外,还要通过缓存一致性协议(MESI)将数据写回到主内存,其他工作内存嗅探后,把自己的工作内存数据过期,重新从主内存读取最新的数据

不可见变可见:

  1. public class TestVolatile {
  2. public static void main(String[] args) {
  3. //创建线程任务
  4. ThreadDemo td = new ThreadDemo();
  5. Thread t = new Thread(td);//创建线程
  6. t.start();
  7. //main线程中也需要使用flag变量
  8. while (true) {
  9. if (td.getFlag()) {//false-true
  10. System.out.println("main---------------");
  11. break;
  12. }
  13. }
  14. }
  15. }
  1. public class ThreadDemo implements Runnable{
  2. /*
  3. volatile 修饰的变量,在一个线程中被修改后,对其它线程立即可见
  4. 禁止cpu对指令重排序
  5. */
  6. privatevolatile)选加 boolean flag = false;//共享数据
  7. public void run() {
  8. try {
  9. Thread.sleep(200);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. this.flag = true;//让一个线程修改共享变量值
  14. System.out.println(this.flag);
  15. }
  16. public boolean getFlag() {
  17. return flag;
  18. }
  19. public void setFlag(boolean flag) {
  20. this.flag = flag;
  21. }
  22. }

没加volatile之前

当共享变量修改后,main方法并不知道变量已经修改,一致在重复着死循环

加volatile后

当共享变量修改后,main方法立刻知道变量已经修改,跳出死循环

无序变有序

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/526437
推荐阅读
相关标签