并发的细节
本文只想说明3个结论
1、synchronized关键字使用MONITORENTER、MONITOREXIT指令实现。
2、多个线程由于synchronized等待,那么执行顺序是FILO。
3、使用ReentrantLock是正常队列FIFO顺序。
如果您对细节感兴趣可以细看。
示例
关键java并发的一个小细节。先从一段代码说起。
public void doWork(String id) {
synchronized (this) {
if (StringUtils.isEmpty(id)) { try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()); } }
synchronized原理
上面是一段非常简单控制并发的java代码。从class字节码可以看到使用了MONITORENTER、MONITOREXIT两个关键指令。
// access flags 0x1
public doWork(Ljava/lang/String;)V //对应方法 // parameter id TRYCATCHBLOCK L0 L1 L2 java/lang/InterruptedException TRYCATCHBLOCK L3 L4 L5 null TRYCATCHBLOCK L5 L6 L5 null L7 LINENUMBER 7 L7 ALOAD 0 //加载this变量 DUP //运行区数据作为Raw Type数据,压入栈顶 ASTORE 2 //this 到局部变量3 MONITORENTER //对应的synchronized L3 LINENUMBER 8 L3 ALOAD 1 //加载局部变量 id INVOKESTATIC org/apache/commons/lang3/StringUtils.isEmpty (Ljava/lang/CharSequence;)Z //调用静态方法 IFEQ L8 //通过栈顶数值判断 L0 LINENUMBER 10 L0 LDC 10000 //加载常量至栈顶 INVOKESTATIC java/lang/Thread.sleep (J)V //静态方法 L1 LINENUMBER 13 L1 GOTO L8 L2 LINENUMBER 11 L2 FRAME FULL [com/ruijie/groupview/port/adpter/web/contorllers/A java/lang/String java/lang/Object] [java/lang/InterruptedException] ASTORE 3 //第三个变量到栈顶 L9 LINENUMBER 12 L9 //打印异常信息 ALOAD 3 //加载第三个变量 INVOKEVIRTUAL java/lang/InterruptedException.printStackTrace ()V L8 // 打印线程名 LINENUMBER 15 L8 FRAME SAME GETSTATIC java/lang/System.out : Ljava/io/PrintStream; INVOKESTATIC java/lang/Thread.currentThread ()Ljava/lang/Thread; INVOKEVIRTUAL java/lang/Thread.getName ()Ljava/lang/String; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L10 LINENUMBER 16 L10 //原文第16行,方法执行完毕。 ALOAD 2 MONITOREXIT //退出monitor L4 GOTO L11 L5 FRAME SAME1 java/lang/Throwable ASTORE 4 ALOAD 2 MONITOREXIT //异常退出monitor L6 ALOAD 4 ATHROW //异常 L11 LINENUMBER 17 L11 FRAME CHOP 1 RETURN //返回 L12 //常量 LOCALVARIABLE e Ljava/lang/InterruptedException; L9 L8 3 LOCALVARIABLE this Lcom/ruijie/groupview/port/adpter/web