对于多线程编程而言其实老生成谈的就是数据同步问题,接下来就会开始接触这块的东东,比较麻烦,但是也是非常重要,所以按部就班的一点点去专研它,下面开始。

数据同步引入:

这里用之前写过的银行叫号的功能做为数据同步知识的引入,具体可以查看: http://www.cnblogs.com/webor2006/p/7709647.html,先复习一下代码:

  1. public class TicketWindowRunnable implements Runnable {
  2. private static final int MAX = 50;//最大号
  3. private int index = 1;
  4.  
  5. @Override
  6. public void run() {
  7. while (index < MAX) {
  8. System.out.println(Thread.currentThread().getName() + "当前的号码是:" + (index++));
  9. }
  10. }
  11. }
  1. /**
  2. * 银行
  3. */
  4. public class Bank {
  5. public static void main(String[] args) {
  6. TicketWindowRunnable ticketWindowRunnable = new TicketWindowRunnable();
  7.  
  8. Thread window1 = new Thread(ticketWindowRunnable, "一号窗口");
  9. window1.start();
  10.  
  11. Thread window2 = new Thread(ticketWindowRunnable, "二号窗口");
  12. window2.start();
  13.  
  14. Thread window3 = new Thread(ticketWindowRunnable, "三号窗口");
  15. window3.start();
  16. }
  17. }

不过这里为了说明同步问题对TicketWindowRunnable稍加变换一下形式:

编译运行:

  1. 柜台:一号柜台,当前的号码是:1
  2. 柜台:一号柜台,当前的号码是:4
  3. 柜台:一号柜台,当前的号码是:5
  4. 柜台:一号柜台,当前的号码是:6
  5. 柜台:一号柜台,当前的号码是:7
  6. 柜台:一号柜台,当前的号码是:8
  7. 柜台:一号柜台,当前的号码是:9
  8. 柜台:三号柜台,当前的号码是:3
  9. 柜台:二号柜台,当前的号码是:2
  10. 柜台:三号柜台,当前的号码是:11
  11. 柜台:三号柜台,当前的号码是:13
  12. 柜台:一号柜台,当前的号码是:10
  13. 柜台:三号柜台,当前的号码是:14
  14. 柜台:二号柜台,当前的号码是:12
  15. 柜台:二号柜台,当前的号码是:17
  16. 柜台:三号柜台,当前的号码是:16
  17. 柜台:一号柜台,当前的号码是:15
  18. 柜台:一号柜台,当前的号码是:20
  19. 柜台:一号柜台,当前的号码是:21
  20. 柜台:三号柜台,当前的号码是:19
  21. 柜台:二号柜台,当前的号码是:18
  22. 柜台:二号柜台,当前的号码是:24
  23. 柜台:二号柜台,当前的号码是:25
  24. 柜台:二号柜台,当前的号码是:26
  25. 柜台:二号柜台,当前的号码是:27
  26. 柜台:二号柜台,当前的号码是:28
  27. 柜台:二号柜台,当前的号码是:29
  28. 柜台:二号柜台,当前的号码是:30
  29. 柜台:二号柜台,当前的号码是:31
  30. 柜台:二号柜台,当前的号码是:32
  31. 柜台:二号柜台,当前的号码是:33
  32. 柜台:二号柜台,当前的号码是:34
  33. 柜台:二号柜台,当前的号码是:35
  34. 柜台:二号柜台,当前的号码是:36
  35. 柜台:二号柜台,当前的号码是:37
  36. 柜台:二号柜台,当前的号码是:38
  37. 柜台:二号柜台,当前的号码是:39
  38. 柜台:二号柜台,当前的号码是:40
  39. 柜台:二号柜台,当前的号码是:41
  40. 柜台:二号柜台,当前的号码是:42
  41. 柜台:二号柜台,当前的号码是:43
  42. 柜台:二号柜台,当前的号码是:44
  43. 柜台:二号柜台,当前的号码是:45
  44. 柜台:二号柜台,当前的号码是:46
  45. 柜台:二号柜台,当前的号码是:47
  46. 柜台:二号柜台,当前的号码是:48
  47. 柜台:二号柜台,当前的号码是:49
  48. 柜台:三号柜台,当前的号码是:23
  49. 柜台:一号柜台,当前的号码是:22

这其实也就是由于线程的同步问题造成的,因为每个线程在执行过程中都有可能被切成Runnable状态,这个结果只是乱序的还无所谓,下面看这种情况:

编译运行:

其原因还是线程的数据同步,那到底这个结果是如何发生的呢?下面用图来解释一下:

这就是为什么500、501会被打印出来,这就是典型线程同步问题造成,如何解决这种总是呢?使用同步块,如下:

再编译运行:

无论执行多少次其结果都一样,而且是按顺序输出的,并不会溢出,这就是关于线程同步的一个简单引入。

结合jconsole,jstack以及汇编指令认识synchronized关键字:

在上面线程同步中使用到了"synchronized"关键字,那它到底是啥东东呢?用一个形象的现实例子:就像咱们去景点游玩得买票,有多很游客都拿着票在进门口,在进门口会有检票员进行检票,进行验票通过之后则游客一个个进入景区进行游览观光,一次只允许一个人通过,其synchronized的意义就类似于检票员,让多线程进行串行化的运行,那么问题来了:难道在synchronized代码块中多线程会变成单线程么?确实就是单线程处理的,为了线程的正确执行,所以说使用synchronized之后会影响程序的执行效率,在效率和正确性面前当然正确性更重要一些啦。

那synchronized到底是个什么东东呢?下面从几个角度来分析一下,先新建一个程序:

然后运行,接下来用三种方式来进一步理解这个关键字:

jconsole:

这时打开jconsole工具来查看一下线程情况:

jstack:

先用jps来查看一下咱们运行的测试程序的进程ID:

然后再用jstack命令来查看:

  1. xiongweideMacBook-Pro:LableCoffee xiongwei$ jstack 70524
  2. 2017-12-16 21:21:06
  3. Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.60-b23 mixed mode):
  4.  
  5. "Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007fb70b001800 nid=0x1307 waiting on condition [0x0000000000000000]
  6. java.lang.Thread.State: RUNNABLE
  7.  
  8. "DestroyJavaVM" #12 prio=5 os_prio=31 tid=0x00007fb70a0d2000 nid=0x1a03 waiting on condition [0x0000000000000000]
  9. java.lang.Thread.State: RUNNABLE
  10.  
  11. "Thread-2" #11 prio=5 os_prio=31 tid=0x00007fb709817800 nid=0x4f03 waiting for monitor entry [0x000070000cdee000]
  12. java.lang.Thread.State: BLOCKED (on object monitor)
  13. at com.javaconcurrency.synchronize.SynchronizedTest.lambda$main$0(SynchronizedTest.java:11)
  14. - waiting to lock <0x0000000795778a40> (a java.lang.Object)
  15. at com.javaconcurrency.synchronize.SynchronizedTest$$Lambda$1/1149319664.run(Unknown Source)
  16. at java.lang.Thread.run(Thread.java:745)
  17.  
  18. "Thread-1" #10 prio=5 os_prio=31 tid=0x00007fb70b0e0000 nid=0x4d03 waiting for monitor entry [0x000070000cceb000]
  19. java.lang.Thread.State: BLOCKED (on object monitor)
  20. at com.javaconcurrency.synchronize.SynchronizedTest.lambda$main$0(SynchronizedTest.java:11)
  21. - waiting to lock <0x0000000795778a40> (a java.lang.Object)
  22. at com.javaconcurrency.synchronize.SynchronizedTest$$Lambda$1/1149319664.run(Unknown Source)
  23. at java.lang.Thread.run(Thread.java:745)
  24.  
  25. "Thread-0" #9 prio=5 os_prio=31 tid=0x00007fb70a0d1000 nid=0x4b03 waiting on condition [0x000070000cbe8000]
  26. java.lang.Thread.State: TIMED_WAITING (sleeping)
  27. at java.lang.Thread.sleep(Native Method)
  28. at com.javaconcurrency.synchronize.SynchronizedTest.lambda$main$0(SynchronizedTest.java:11)
  29. - locked <0x0000000795778a40> (a java.lang.Object)
  30. at com.javaconcurrency.synchronize.SynchronizedTest$$Lambda$1/1149319664.run(Unknown Source)
  31. at java.lang.Thread.run(Thread.java:745)
  32.  
  33. "Service Thread" #8 daemon prio=9 os_prio=31 tid=0x00007fb70a83e000 nid=0x4703 runnable [0x0000000000000000]
  34. java.lang.Thread.State: RUNNABLE
  35.  
  36. "C1 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fb70a858000 nid=0x4503 waiting on condition [0x0000000000000000]
  37. java.lang.Thread.State: RUNNABLE
  38.  
  39. "C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fb70a857800 nid=0x4303 waiting on condition [0x0000000000000000]
  40. java.lang.Thread.State: RUNNABLE
  41.  
  42. "C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fb70a855800 nid=0x4103 waiting on condition [0x0000000000000000]
  43. java.lang.Thread.State: RUNNABLE
  44.  
  45. "Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fb70a855000 nid=0x3f0b runnable [0x0000000000000000]
  46. java.lang.Thread.State: RUNNABLE
  47.  
  48. "Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fb709803800 nid=0x3203 in Object.wait() [0x000070000c4d3000]
  49. java.lang.Thread.State: WAITING (on object monitor)
  50. at java.lang.Object.wait(Native Method)
  51. - waiting on <0x00000007955870b8> (a java.lang.ref.ReferenceQueue$Lock)
  52. at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
  53. - locked <0x00000007955870b8> (a java.lang.ref.ReferenceQueue$Lock)
  54. at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
  55. at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
  56.  
  57. "Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fb70b028800 nid=0x3003 in Object.wait() [0x000070000c3d0000]
  58. java.lang.Thread.State: WAITING (on object monitor)
  59. at java.lang.Object.wait(Native Method)
  60. - waiting on <0x0000000795586af8> (a java.lang.ref.Reference$Lock)
  61. at java.lang.Object.wait(Object.java:502)
  62. at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
  63. - locked <0x0000000795586af8> (a java.lang.ref.Reference$Lock)
  64.  
  65. "VM Thread" os_prio=31 tid=0x00007fb70a018000 nid=0x2e03 runnable
  66.  
  67. "GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fb70a014800 nid=0x2607 runnable
  68.  
  69. "GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fb70a808000 nid=0x2803 runnable
  70.  
  71. "GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fb70a808800 nid=0x2a03 runnable
  72.  
  73. "GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fb70a809000 nid=0x2c03 runnable
  74.  
  75. "VM Periodic Task Thread" os_prio=31 tid=0x00007fb70980a000 nid=0x4903 waiting on condition
  76.  
  77. JNI global references: 308

其中只要咱们的三个运行的线程,可以发现目前Thread-0已经开始休眠了,也就是拿到了同步锁开始执行程序了,而其它两个线程全部处理等待状态,通过这也可以清楚的体现到synchronized关键字的作用。

反汇编指令:

那synchronized关键字在java字节反汇编里表现又如何呢?这里可以通过java的一个命令来查看字符码对应的反汇编指令,具体使用如下:

接着用javap命令来查看反汇编指令,如下:

  1. xiongweideMacBook-Pro:synchronize xiongwei$ javap -c TicketWindowRunnable.class
  2. Compiled from "TicketWindowRunnable.java"
  3. public class com.javaconcurrency.synchronize.TicketWindowRunnable implements java.lang.Runnable {
  4. public com.javaconcurrency.synchronize.TicketWindowRunnable();
  5. Code:
  6. 0: aload_0
  7. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  8. 4: aload_0
  9. 5: iconst_1
  10. 6: putfield #2 // Field index:I
  11. 9: aload_0
  12. 10: new #3 // class java/lang/Object
  13. 13: dup
  14. 14: invokespecial #1 // Method java/lang/Object."<init>":()V
  15. 17: putfield #4 // Field MONITOR:Ljava/lang/Object;
  16. 20: return
  17.  
  18. public void run();
  19. Code:
  20. 0: aload_0
  21. 1: getfield #4 // Field MONITOR:Ljava/lang/Object;
  22. 4: dup
  23. 5: astore_1
  24. 6: monitorenter
  25. 7: aload_0
  26. 8: getfield #2 // Field index:I
  27. 11: sipush 500
  28. 14: if_icmplt 22
  29. 17: aload_1
  30. 18: monitorexit
  31. 19: goto 93
  32. 22: ldc2_w #6 // long 5l
  33. 25: invokestatic #8 // Method java/lang/Thread.sleep:(J)V
  34. 28: goto 36
  35. 31: astore_2
  36. 32: aload_2
  37. 33: invokevirtual #10 // Method java/lang/InterruptedException.printStackTrace:()V
  38. 36: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
  39. 39: new #12 // class java/lang/StringBuilder
  40. 42: dup
  41. 43: invokespecial #13 // Method java/lang/StringBuilder."<init>":()V
  42. 46: invokestatic #14 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
  43. 49: invokevirtual #15 // Method java/lang/Thread.getName:()Ljava/lang/String;
  44. 52: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  45. 55: ldc #17 // String 当前的号码是:
  46. 57: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  47. 60: aload_0
  48. 61: dup
  49. 62: getfield #2 // Field index:I
  50. 65: dup_x1
  51. 66: iconst_1
  52. 67: iadd
  53. 68: putfield #2 // Field index:I
  54. 71: invokevirtual #18 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  55. 74: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  56. 77: invokevirtual #20 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  57. 80: aload_1
  58. 81: monitorexit
  59. 82: goto 90
  60. 85: astore_3
  61. 86: aload_1
  62. 87: monitorexit
  63. 88: aload_3
  64. 89: athrow
  65. 90: goto 0
  66. 93: return
  67. Exception table:
  68. from to target type
  69. 22 28 31 Class java/lang/InterruptedException
  70. 7 19 85 any
  71. 22 82 85 any
  72. 85 88 85 any
  73. }

接着对照着咱们的java源代码来分析:

而对应汇编代码中有这样一句话:

实际上也就对应咱们的同步块代码 :

接着应该程序就结束了,而汇编上也能看到:

所以说通过以上三种方式对synchronized关键字应该有比较深刻的印象了,实际上在在底层是叫Monitor。

java线程基础巩固---数据同步引入并结合jconsole,jstack以及汇编指令认识synchronized关键字的更多相关文章

  1. Java线程安全与数据同步

    import java.util.HashMap; import java.util.concurrent.TimeUnit; public class Test { public static vo ...

  2. 线程安全、数据同步之 synchronized 与 Lock

    本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解. 为什么要 ...

  3. Java 线程基础

    Java 线程基础

  4. java 线程​基本概念 可见性 同步

    开发高性能并发应用不是一件容易的事情.这类应用的例子包括高性能Web服务器.游戏服务器和搜索引擎爬虫等.这样的应用可能需要同时处理成千上万个请求.对于这样的应用,一般采用多线程或事件驱动的架构.对于J ...

  5. Java线程基础知识(状态、共享与协作)

    1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...

  6. 【Java并发专题之二】Java线程基础

    使用线程更好的提高资源利用率,但也会带来上下文切换的消耗,频繁的内核态和用户态的切换消耗,如果代码设计不好,可能弊大于利. 一.线程 进程是分配资源的最小单位,线程是程序执行的最小单位:线程是依附于进 ...

  7. Java 线程基础知识

    前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...

  8. Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解

       我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于 ...

  9. java实现高性能的数据同步

    最近在做一个银行的生产数据脱敏系统,今天写代码时遇到了一个“瓶颈”,脱敏系统需要将生产环境上Infoxmix里的数据原封不动的Copy到另一台 Oracle数据库服务器上,然后对Copy后的数据作些漂 ...

随机推荐

  1. Unity热更新 AssetBundle

    在游戏开发中,常常需要用到热更新技术.比如:一个手机游戏开发好后,用户安装到手机上.如果此时我们要更新一个新的功能,如果没有热更新,那么需要用户卸载掉手机上的游戏,然后安装新的包,这样做十分麻烦,而且 ...

  2. 架构模式: API网关

    模式: API网关 上下文 让我们假设您正在构建一个使用Microservice体系结构模式的在线商店,并且您正在实现产品详细信息页面.您需要开发产品详细信息用户界面的多个版本: 用于桌面和移动浏览器 ...

  3. 李宏毅 Tensorflow解决Fizz Buzz问题

    提出问题 一个网友的博客,记录他在一次面试时,碰到面试官要求他在白板上用TensorFlow写一个简单的网络实现异或(XOR)功能.这个本身并不难,单层感知器不能解决异或问题是学习神经网络中的一个常识 ...

  4. C学习笔记-小程序(长期更新)

    产生随机数 int t = (int)time(NULL); srand(t); int num = rand() % 10; 利用keybd_event函数自动打印,mouse_event函数保存文 ...

  5. 在Linux虚拟机里开启Apache服务

    首先第一步我们配置环境:把yum与Linux ping通 1.我们来下载apache服务,输入:yum install httpd * 2.安装完毕之后默认是死的,要给他启动一下,输入命令:syste ...

  6. 【Python】【基础知识】【异常】【Python的异常】报错、警告

    Python的异常 异常的层次结构: BaseException [所有异常的基类] +-- SystemExit [解释器请求退出] +-- KeyboardInterrupt [用户中断执行(通常 ...

  7. Wordpress 所有 hook 钩子

    muplugins_loaded 在必须使用的插件加载之后. registered_taxonomy 对于类别,post_tag 等 Registered_post_type 用于帖子,页面等 plu ...

  8. 脚本(bat、shell)调用conda

    官网说明:https://docs.conda.io/projects/conda/en/latest/user-guide/troubleshooting.html#using-conda-in-w ...

  9. 剑指offer6:旋转数组的最小数字

    1. 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一 ...

  10. Minimum Cut(2015沈阳online)【贪心】

    Minimum Cut[贪心]2015沈阳online 题意:割最少的边使得图不连通,并且割掉的边中有且仅有一条是生成树的边. 首先,我们选择一条树中的边进行切割,此时仅考虑树上的边集,有两种情况:1 ...