这些天忙着写业务代码,曹工说Tomcat系列暂时没时间写,先随便写点其余的。html
逛博客园的时候,发现一篇园友的阿里面试文章,http://www.javashuo.com/article/p-aaxpqizc-kz.html。java
里面提到了:两个线程,交替打印奇偶数这道笔试题。git
看了园友实现的代码(https://github.com/crossoverJie/JCSprout/blob/master/src/main/java/com/crossoverjie/actual/TwoThread.java),感受有点复杂,因而本身琢磨着写了一下,如下三个版本,一个基于object的wait、notify,一个基于volatile变量的方式,最后一种和第二种类似,只是用了unsafe实现。github
1 package producerconsumer; 2
3 import java.util.concurrent.atomic.AtomicInteger; 4
5 public class OddEvenThread { 6 private static volatile Integer counter = 0; 7 private static Object monitor = new Object(); 8
9 public static void main(String[] args) { 10 new Thread(new Runnable() { 11 // 奇数线程
12 @Override 13 public void run() { 14 while (true){ 15 synchronized (monitor){ 16 if (counter % 2 != 0){ 17 continue; 18 } 19 int i = ++counter; 20 if (i > 100){ 21 return; 22 } 23 System.out.println("奇数线程:" + i); 24 try { 25 monitor.notify(); 26 monitor.wait(); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 } 31 } 32 } 33 }).start(); 34
35 new Thread(new Runnable() { 36 @Override 37 public void run() { 38 while (true){ 39 synchronized (monitor){ 40 if (counter % 2 == 0){ 41 continue; 42 } 43 int i = ++counter; 44 if (i > 100){ 45 return; 46 } 47 System.out.println("偶数线程:" + i); 48 try { 49 monitor.notify(); 50 monitor.wait(); 51 } catch (InterruptedException e) { 52 e.printStackTrace(); 53 } 54 } 55 } 56 } 57 }).start(); 58
59
60 } 61 }
思路很简单,代码也很简单,主要就是基于 synchronized 锁来实现阻塞和唤醒。面试
可是我我的感受,频繁地阻塞和唤醒,都须要线程从用户态转入核心态,有点太耗性能了,而后写了如下的自旋非阻塞版本。ide
该方式的思路是,线程在volatile变量上无限循环,直到volatile变量变为false。变为false后,线程开始真正地执行业务逻辑,打印数字,最后,须要挂起本身,并修改volatile变量,来唤醒其余线程。oop
1 package producerconsumer; 2
3 /**
4 * Created by Administrator on 2019/7/20. 5 */
6 public class OddEvenThreadVolatileVersion { 7 private static volatile boolean loopForOdd = true; 8
9 private static volatile boolean loopForEven = true; 10
11 private static volatile int counter = 1; 12
13 public static void main(String[] args) throws InterruptedException { 14 new Thread(new Runnable() { 15
16 // 奇数线程
17 @Override 18 public void run() { 19 while (true) { 20 while (loopForOdd){ 21
22 } 23
24 int counter = OddEvenThreadVolatileVersion.counter; 25 if (counter > 100) { 26 break; 27 } 28 System.out.println("奇数线程:" + counter); 29
30 OddEvenThreadVolatileVersion.counter++; 31
32 // 修改volatile,通知偶数线程中止循环,同时,准备让本身陷入循环
33 loopForEven = false; 34
35 loopForOdd = true; 36
37 } 38
39 } 40 }).start(); 41
42 new Thread(new Runnable() { 43 @Override 44 public void run() { 45 while (true) { 46 while (loopForEven) { 47
48 } 49
50 int counter = OddEvenThreadVolatileVersion.counter; 51 if (counter > 100) { 52 break; 53 } 54 System.out.println("偶数线程:" + counter); 55
56 OddEvenThreadVolatileVersion.counter++; 57
58 // 修改volatile,通知奇数线程中止循环,同时,准备让本身陷入循环
59 loopForOdd = false; 60
61 loopForEven = true; 62 } 63 } 64 }).start(); 65
66 // 先启动奇数线程
67 loopForOdd = false; 68
69 } 70 }
1 package producerconsumer; 2
3 import sun.misc.Unsafe; 4
5 import java.lang.reflect.Field; 6
7 /**
8 * Created by Administrator on 2019/7/20. 9 */
10 public class OddEvenThreadCASVersion { 11 private static volatile boolean loopForOdd = true; 12
13 private static volatile boolean loopForEven = true; 14
15 private static long loopForOddOffset; 16
17 private static long loopForEvenOffset; 18
19 private static volatile int counter = 1; 20
21 private static Unsafe unsafe; 22
23 static { 24 Field theUnsafeInstance = null; 25 try { 26 theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); 27 } catch (NoSuchFieldException e) { 28 e.printStackTrace(); 29 } 30 theUnsafeInstance.setAccessible(true); 31 try { 32 unsafe = (Unsafe) theUnsafeInstance.get(Unsafe.class); 33 } catch (IllegalAccessException e) { 34 e.printStackTrace(); 35 } 36
37 try { 38 loopForOddOffset = unsafe.staticFieldOffset 39 (OddEvenThreadCASVersion.class.getDeclaredField("loopForOdd")); 40 } catch (Exception ex) { throw new Error(ex); } 41
42 try { 43 loopForEvenOffset = unsafe.staticFieldOffset 44 (OddEvenThreadCASVersion.class.getDeclaredField("loopForEven")); 45 } catch (Exception ex) { throw new Error(ex); } 46 } 47
48 public static void main(String[] args) throws InterruptedException { 49 new Thread(new Runnable() { 50
51 // 奇数线程
52 @Override 53 public void run() { 54 while (true) { 55 while (true){ 56 boolean b = unsafe.getBoolean(OddEvenThreadCASVersion.class, loopForOddOffset); 57 if (b){ 58 // 循环
59 }else { 60 break; 61 } 62 } 63
64 int counter = OddEvenThreadCASVersion.counter; 65 if (counter > 100) { 66 break; 67 } 68 System.out.println("奇数线程:" + counter); 69
70 OddEvenThreadCASVersion.counter++; 71
72 // 修改volatile,通知偶数线程中止循环,同时,准备让本身陷入循环
73 unsafe.putBoolean(OddEvenThreadCASVersion.class, loopForOddOffset,true); 74 unsafe.putBoolean(OddEvenThreadCASVersion.class, loopForEvenOffset,false); 75
76 } 77
78 } 79 }).start(); 80
81 new Thread(new Runnable() { 82 @Override 83 public void run() { 84 while (true) { 85 while (true){ 86 boolean b = unsafe.getBoolean(OddEvenThreadCASVersion.class, loopForEvenOffset); 87 if (b){ 88 // 循环
89 }else { 90 break; 91 } 92 } 93
94 int counter = OddEvenThreadCASVersion.counter; 95 if (counter > 100) { 96 break; 97 } 98 System.out.println("偶数线程:" + counter); 99
100 OddEvenThreadCASVersion.counter++; 101
102 // 修改volatile,通知奇数线程中止循环,同时,准备让本身陷入循环
103 unsafe.putBoolean(OddEvenThreadCASVersion.class, loopForOddOffset,false); 104 unsafe.putBoolean(OddEvenThreadCASVersion.class, loopForEvenOffset,true); 105 } 106 } 107 }).start(); 108
109 // 先启动奇数线程
110 loopForOdd = false; 111
112 } 113 }
代码总体和第二种相似,只是为了学习下 unsafe 的使用。unsafe的操做方式,若是学过c语言的话,应该会以为比较熟悉,里面的offset,其实就相似与指针的位置。性能
咱们看看,要获取一个值,用unsafe的写法是,unsafe.getBoolean(OddEvenThreadCASVersion.class, loopForEvenOffset),模拟成c语言就是,获取到 OddEvenThreadCASVersion 的指针,再偏移 loopForEvenOffset,再取接下来的4个字节,换算成 boolean便可。学习
void * ptr = &OddEvenThreadCASVersion.class
int tmp = *(int*)(ptr + loopForEvenOffset)
boolean ret = (boolean)tmp;
(只是个示意,不用纠结哈,c语言快忘完了。。)atom
ps:注意上面变红部分,由于是static field,因此要用这个方法,不然用 public native long objectFieldOffset(Field var1)。
可重入锁的实现方式相似,这里留给读者进行实践。 你们有什么好的思路,能够在下方进行评论,也欢迎加群探讨。