今天开始学习Java多线程源码,并将学习到的东西记录下来,由于个人能力有限,文中难免有错误之处,希望看到我博客的园友能指出我的错误,在此表示感谢!同时希望自己能坚持下来,加油!
学习java多线程需要先了解一些原理性的东西,比如内存可见性,原子性,重排序,Java内存模型等等,由于是在学习Java多线程源码,所以我不会写这些东西,等以后有机会再写成博客,这个系列主要学习java.util.concurrent包下的源码,首先学习java中的锁,也就是java.util.concurrent.locks下的类,下图是locks包下得类结构:
这个包下最核心的类是AbstractQueuedSynchronizer类,这个类就是实现同步器,Java中锁的实现都是通过内聚一个同步器来实现的;所以学习Java的锁,首先需要学习同步器的作用和原理,在学习同步器之前,先学习LockSupport类,这个类中的方法主要用于阻塞线程和唤醒线程。
1 // Unsafe成员变量 2 private static final sun.misc.Unsafe UNSAFE; 3 // Thread类的parkBlocker的偏移地址 4 private static final long parkBlockerOffset; 5 // Thread类的threadLocalRandomSeed的偏移地址 6 private static final long SEED; 7 // Thread类的threadLocalRandomProbe的偏移地址 8 private static final long PROBE; 9 // Thread类的threadLocalRandomSecondarySeed的偏移地址 10 private static final long SECONDARY; 11 // 以上long类型的都是内存偏移地址,通过该偏移地址就可以获取或设置该值 12 static { 13 try { 14 UNSAFE = sun.misc.Unsafe.getUnsafe(); 15 Class<?> tk = Thread.class; 16 // UNSAFE.objectFieldOffset用于获取字段的偏移地址 17 parkBlockerOffset = UNSAFE.objectFieldOffset 18 (tk.getDeclaredField("parkBlocker")); 19 SEED = UNSAFE.objectFieldOffset 20 (tk.getDeclaredField("threadLocalRandomSeed")); 21 PROBE = UNSAFE.objectFieldOffset 22 (tk.getDeclaredField("threadLocalRandomProbe")); 23 SECONDARY = UNSAFE.objectFieldOffset 24 (tk.getDeclaredField("threadLocalRandomSecondarySeed")); 25 } catch (Exception ex) { throw new Error(ex); } 26 }
每一个线程(Thread)都会有parkBlocker等成员变量,其中parkBlocker表示线程阻塞在哪个对象上,在分析问题时可以通过打印该成员变量来查看线程阻塞在哪个对象上,其余几个变量的含义不太清楚;这里通过UNSAFE.objectFieldOffset可以获取字段相对于线程对象的内存偏移,通过这个内存偏移可以快速的获取或者设置该成员变量的值。
1 // 获取线程t的parkBlocker 2 public static Object getBlocker(Thread t) { 3 if (t == null) 4 throw new NullPointerException(); 5 return UNSAFE.getObjectVolatile(t, parkBlockerOffset); 6 } 7 8 // 设置线程t的parkBlocker字段的值为arg 9 private static void setBlocker(Thread t, Object arg) { 10 // parkBlockerOffset为parkBlocker的内存偏移 11 UNSAFE.putObject(t, parkBlockerOffset, arg); 12 } 13 14 public static void park() { 15 // UNISAFE.part方法用于阻塞线程,第一个参数表示是否为绝对时间,第二个参数为0表示无限等待 16 UNSAFE.park(false, 0L); 17 } 18 19 public static void park(Object blocker) { 20 Thread t = Thread.currentThread(); 21 // 设置线程t的blocker字段 22 setBlocker(t, blocker); 23 // 阻塞线程,所以下面的setBlocker不会接着执行 24 UNSAFE.park(false, 0L); 25 // 只有当调用unpark方法或者线程被中断时才会执行下面第二个setBlocker方法 26 setBlocker(t, null); 27 } 28 29 // nanos为超时时间 30 public static void parkNanos(long nanos) { 31 if (nanos > 0) 32 UNSAFE.park(false, nanos); 33 } 34 35 public static void parkNanos(Object blocker, long nanos) { 36 if (nanos > 0) { 37 Thread t = Thread.currentThread(); 38 setBlocker(t, blocker); 39 UNSAFE.park(false, nanos); 40 setBlocker(t, null); 41 } 42 } 43 44 public static void parkUntil(long deadline) { 45 // 这里使用的时绝对时间 46 UNSAFE.park(true, deadline); 47 } 48 49 public static void parkUntil(Object blocker, long deadline) { 50 Thread t = Thread.currentThread(); 51 setBlocker(t, blocker); 52 UNSAFE.park(true, deadline); 53 setBlocker(t, null); 54 }
阻塞线程的方法可以分两大类,一类是不设置parkBlocker的,一类是设置parkBlocker的。而每一类有三种方法:一种是永久阻塞线程,直到线程被唤醒或者被中断;一种是增加了超时时间,使用的是相对时间;一种是增加了超时时间,但使用的是绝对时间。
1 public static void unpark(Thread thread) { 2 if (thread != null) 3 // UNSAFE.unpark用于唤醒线程,但使用该方法必须保证传入的thread线程必须存在 4 UNSAFE.unpark(thread); 5 }
在LockSupport类中还有一个方法nextSecondarySeed,这个方法用于生成随机数,但不清楚有什么用处,有知道的园友希望能告诉我。
原文:http://www.cnblogs.com/stanley-cn/p/6662104.html