├── .gitignore ├── Chapter01 ├── CommonSet.md ├── DelayQueue.md ├── Deque.md ├── More.md ├── Queue.md ├── README.md └── ThreadSafe.md ├── Chapter02 ├── Example.md ├── HowToUse.md └── README.md ├── Chapter03 ├── CreateThreadPool.md ├── Example.md ├── README.md ├── TaskRun.md └── ThreadPoolExecutor.md ├── Chapter04 ├── CallableFuture.md ├── README.md └── TaskCancle.md ├── Chapter05 ├── Lock-ReentrantLock.md ├── README.md ├── ReadWriteLock.md ├── lockInterruptibly.md └── tryLock.md ├── Chapter06 ├── CyclicBarrier-CountDownLatch.md ├── README.md └── Semaphore.md ├── Chapter07 ├── Example.md ├── README.md └── jdk1.5ScourceCode.md ├── Chapter08 ├── Future.md ├── GuardedSuspension.md ├── Immutable.md ├── MasterWorker.md ├── Producter-Consumer.md ├── README.md └── img │ ├── img01.png │ ├── img02.png │ └── img03.png ├── Chapter09 ├── Condition.md ├── README.md ├── ReadWriteLock.md ├── ReentranLock.md ├── Semaphore.md ├── ThreadLocal.md ├── synchronized.md └── volatile.md ├── Chapter10 └── README.md ├── Chapter11 └── README.md ├── Chapter12 ├── README.md └── img │ └── img1.png ├── README.md └── SUMMARY.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | _book/Chapter01/.DS_Store 3 | _book/ 4 | 5 | Chapter01/.DS_Store 6 | -------------------------------------------------------------------------------- /Chapter01/CommonSet.md: -------------------------------------------------------------------------------- 1 | 常用的集合结构 2 | ============ 3 | 4 | ``` 5 | Collection 6 | --List: 将以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。 7 | --ArrayList / LinkedList / Vector 8 | --Set : 不能含有重复的元素 9 | --HashSet / TreeSet 10 | Map 11 | --HashMap 12 | --HashTable 13 | --TreeMap 14 | ``` -------------------------------------------------------------------------------- /Chapter01/DelayQueue.md: -------------------------------------------------------------------------------- 1 | 精巧好用的DelayQueue 2 | ========= 3 | 4 | 原文:http://www.cnblogs.com/jobs/archive/2007/04/27/730255.html 5 | 6 | 我们谈一下实际的场景吧。我们在开发中,有如下场景 7 | 8 | a) 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。 9 | 10 | b) 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。 11 | 12 | c) 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。 13 | 14 | 15 | 一种笨笨的办法就是,使用一个后台线程,遍历所有对象,挨个检查。这种笨笨的办法简单好用,但是对象数量过多时,可能存在性能问题,检查间隔时间不好设置,间隔时间过大,影响精确度,多小则存在效率问题。而且做不到按超时的时间顺序处理。 16 | 17 | 这场景,使用DelayQueue最适合了。 18 | 19 | 20 | DelayQueue是一个BlockingQueue,其特化的参数是Delayed。(不了解BlockingQueue的同学,先去了解BlockingQueue再看本文) 21 | Delayed扩展了Comparable接口,比较的基准为延时的时间值,Delayed接口的实现类getDelay的返回值应为固定值(final)。DelayQueue内部是使用PriorityQueue实现的。 22 | 23 | DelayQueue = BlockingQueue + PriorityQueue + Delayed 24 | 25 | DelayQueue的关键元素BlockingQueue、PriorityQueue、Delayed。可以这么说,DelayQueue是一个使用优先队列(PriorityQueue)实现的BlockingQueue,优先队列的比较基准值是时间。 26 | 27 | 他们的基本定义如下 28 | 29 | ```java 30 | public interface Comparable { 31 | public int compareTo(T o); 32 | } 33 | 34 | public interface Delayed extends Comparable { 35 | long getDelay(TimeUnit unit); 36 | } 37 | 38 | public class DelayQueue implements BlockingQueue { 39 | private final PriorityQueue q = new PriorityQueue(); 40 | } 41 | ``` 42 | 43 | 44 | DelayQueue内部的实现使用了一个优先队列。当调用DelayQueue的offer方法时,把Delayed对象加入到优先队列q中。如下: 45 | 46 | ```java 47 | public boolean offer(E e) { 48 | final ReentrantLock lock = this.lock; 49 | lock.lock(); 50 | try { 51 | E first = q.peek(); 52 | q.offer(e); 53 | if (first == null || e.compareTo(first) < 0) 54 | available.signalAll(); 55 | return true; 56 | } finally { 57 | lock.unlock(); 58 | } 59 | } 60 | ``` 61 | 62 | DelayQueue的take方法,把优先队列q的first拿出来(peek),如果没有达到延时阀值,则进行await处理。如下: 63 | 64 | ```java 65 | public E take() throws InterruptedException { 66 | final ReentrantLock lock = this.lock; 67 | lock.lockInterruptibly(); 68 | try { 69 | for (;;) { 70 | E first = q.peek(); 71 | if (first == null) { 72 | available.await(); 73 | } else { 74 | long delay = first.getDelay(TimeUnit.NANOSECONDS); 75 | if (delay > 0) { 76 | long tl = available.awaitNanos(delay); 77 | } else { 78 | E x = q.poll(); 79 | assert x != null; 80 | if (q.size() != 0) 81 | available.signalAll(); // wake up other takers 82 | return x; 83 | 84 | } 85 | } 86 | } 87 | } finally { 88 | lock.unlock(); 89 | } 90 | } 91 | ``` 92 | 93 | 以下是Sample,是一个缓存的简单实现。共包括三个类Pair、DelayItem、Cache。如下: 94 | 95 | public class Pair { 96 | public K first; 97 | 98 | public V second; 99 | 100 | public Pair() {} 101 | 102 | public Pair(K first, V second) { 103 | this.first = first; 104 | this.second = second; 105 | } 106 | } 107 | 108 | 以下是Delayed的实现 109 | 110 | ```java 111 | import java.util.concurrent.Delayed; 112 | import java.util.concurrent.TimeUnit; 113 | import java.util.concurrent.atomic.AtomicLong; 114 | 115 | public class DelayItem implements Delayed { 116 | /** Base of nanosecond timings, to avoid wrapping */ 117 | private static final long NANO_ORIGIN = System.nanoTime(); 118 | 119 | /** 120 | * Returns nanosecond time offset by origin 121 | */ 122 | final static long now() { 123 | return System.nanoTime() - NANO_ORIGIN; 124 | } 125 | 126 | /** 127 | * Sequence number to break scheduling ties, and in turn to guarantee FIFO order among tied 128 | * entries. 129 | */ 130 | private static final AtomicLong sequencer = new AtomicLong(0); 131 | 132 | /** Sequence number to break ties FIFO */ 133 | private final long sequenceNumber; 134 | 135 | /** The time the task is enabled to execute in nanoTime units */ 136 | private final long time; 137 | 138 | private final T item; 139 | 140 | public DelayItem(T submit, long timeout) { 141 | this.time = now() + timeout; 142 | this.item = submit; 143 | this.sequenceNumber = sequencer.getAndIncrement(); 144 | } 145 | 146 | public T getItem() { 147 | return this.item; 148 | } 149 | 150 | public long getDelay(TimeUnit unit) { 151 | long d = unit.convert(time - now(), TimeUnit.NANOSECONDS); 152 | return d; 153 | } 154 | 155 | public int compareTo(Delayed other) { 156 | if (other == this) // compare zero ONLY if same object 157 | return 0; 158 | if (other instanceof DelayItem) { 159 | DelayItem x = (DelayItem) other; 160 | long diff = time - x.time; 161 | if (diff < 0) 162 | return -1; 163 | else if (diff > 0) 164 | return 1; 165 | else if (sequenceNumber < x.sequenceNumber) 166 | return -1; 167 | else 168 | return 1; 169 | } 170 | long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS)); 171 | return (d == 0) ? 0 : ((d < 0) ? -1 : 1); 172 | } 173 | } 174 | ``` 175 | 176 | 177 | 以下是Cache的实现,包括了put和get方法,还包括了可执行的main函数。 178 | ``` 179 | import java.util.concurrent.ConcurrentHashMap; 180 | import java.util.concurrent.ConcurrentMap; 181 | import java.util.concurrent.DelayQueue; 182 | import java.util.concurrent.TimeUnit; 183 | import java.util.logging.Level; 184 | import java.util.logging.Logger; 185 | 186 | public class Cache { 187 | private static final Logger LOG = Logger.getLogger(Cache.class.getName()); 188 | 189 | private ConcurrentMap cacheObjMap = new ConcurrentHashMap(); 190 | 191 | private DelayQueue>> q = new DelayQueue>>(); 192 | 193 | private Thread daemonThread; 194 | 195 | public Cache() { 196 | 197 | Runnable daemonTask = new Runnable() { 198 | public void run() { 199 | daemonCheck(); 200 | } 201 | }; 202 | 203 | daemonThread = new Thread(daemonTask); 204 | daemonThread.setDaemon(true); 205 | daemonThread.setName("Cache Daemon"); 206 | daemonThread.start(); 207 | } 208 | 209 | private void daemonCheck() { 210 | 211 | if (LOG.isLoggable(Level.INFO)) 212 | LOG.info("cache service started."); 213 | 214 | for (;;) { 215 | try { 216 | DelayItem> delayItem = q.take(); 217 | if (delayItem != null) { 218 | // 超时对象处理 219 | Pair pair = delayItem.getItem(); 220 | cacheObjMap.remove(pair.first, pair.second); // compare and remove 221 | } 222 | } catch (InterruptedException e) { 223 | if (LOG.isLoggable(Level.SEVERE)) 224 | LOG.log(Level.SEVERE, e.getMessage(), e); 225 | break; 226 | } 227 | } 228 | 229 | if (LOG.isLoggable(Level.INFO)) 230 | LOG.info("cache service stopped."); 231 | } 232 | 233 | // 添加缓存对象 234 | public void put(K key, V value, long time, TimeUnit unit) { 235 | V oldValue = cacheObjMap.put(key, value); 236 | if (oldValue != null) 237 | q.remove(key); 238 | 239 | long nanoTime = TimeUnit.NANOSECONDS.convert(time, unit); 240 | q.put(new DelayItem>(new Pair(key, value), nanoTime)); 241 | } 242 | 243 | public V get(K key) { 244 | return cacheObjMap.get(key); 245 | } 246 | 247 | // 测试入口函数 248 | public static void main(String[] args) throws Exception { 249 | Cache cache = new Cache(); 250 | cache.put(1, "aaaa", 3, TimeUnit.SECONDS); 251 | 252 | Thread.sleep(1000 * 2); 253 | { 254 | String str = cache.get(1); 255 | System.out.println(str); 256 | } 257 | 258 | Thread.sleep(1000 * 2); 259 | { 260 | String str = cache.get(1); 261 | System.out.println(str); 262 | } 263 | } 264 | } 265 | ``` 266 | 267 | 运行Sample,main函数执行的结果是输出两行,第一行为aaa,第二行为null。 268 | -------------------------------------------------------------------------------- /Chapter01/Deque.md: -------------------------------------------------------------------------------- 1 | 并发Deque 2 | ===== 3 | 4 | 在JDK6中,还提供了一种双端队列(Double-Ended Queue),简称Deque。Deque允许在队列的头部或尾部进行出队和入队操作。 -------------------------------------------------------------------------------- /Chapter01/More.md: -------------------------------------------------------------------------------- 1 | #探索 ConcurrentHashMap 高并发性的实现机制 2 | http://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap/index.html?ca=drs- 3 | 4 | #Java 理论与实践: 并发集合类 5 | http://www.ibm.com/developerworks/cn/java/j-jtp07233/ 6 | 7 | #Java 理论与实践: 构建一个更好的 HashMap 8 | http://www.ibm.com/developerworks/cn/java/j-jtp08223/ 9 | -------------------------------------------------------------------------------- /Chapter01/Queue.md: -------------------------------------------------------------------------------- 1 | 队列:Queue, BlockingQueue 2 | ====== 3 | JDK1.5也增加了两种新的容器类型:Queue和BlockingQueue。 4 | 5 | Queue是用来临时保存一组等待处理的元素。Queue上的操作不会阻塞,如果队列为空,那么获取元素的操作将返回空值。 6 | 7 | BlockingQueue扩展了Queue,增加了可阻塞的插入和获取等级操作。如果队列为空,那么获取元素的操作将一直阻塞,直到队列中出现一个可用的元素。如果队列已满,那么插入元素的操作将一直阻塞,直到队列中出现可用的空间。在生产者、消费者模式中,阻塞队列是非常有用的。 8 | 9 | 所有已知实现类: ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue 10 | 11 | 12 | #基于阻塞队列BlockingQueue的生产者、消费者模式 13 | 14 | ```java 15 | import java.util.concurrent.BlockingQueue; 16 | 17 | public class Producer implements Runnable { 18 | private BlockingQueue queue; 19 | 20 | public Producer(BlockingQueue queue) { 21 | this.queue = queue; 22 | } 23 | 24 | public void run() { 25 | for (int product = 1; product <= 10; product++) { 26 | try { 27 | Thread.sleep((int) Math.random() * 3000); 28 | queue.put(product); 29 | System.out.println("Producer 生产: " + product); 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | } 36 | 37 | import java.util.concurrent.BlockingQueue; 38 | 39 | public class Consumer implements Runnable { 40 | private BlockingQueue queue; 41 | 42 | public Consumer(BlockingQueue queue) { 43 | this.queue = queue; 44 | } 45 | 46 | public void run() { 47 | for (int i = 1; i <= 10; i++) { 48 | try { 49 | Thread.sleep((int) (Math.random() * 3000)); 50 | System.out.println("Consumer 消费: "+ queue.take()); 51 | } catch (InterruptedException e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | } 56 | } 57 | 58 | import java.util.concurrent.ArrayBlockingQueue; 59 | import java.util.concurrent.BlockingQueue; 60 | 61 | public class BlockingQueueDemo { 62 | public static void main(String[] args) { 63 | BlockingQueue queue = new ArrayBlockingQueue(1); 64 | Thread producerThread = new Thread( 65 | new Producer(queue)); 66 | Thread consumerThread = new Thread( 67 | new Consumer(queue)); 68 | producerThread.start(); 69 | consumerThread.start(); 70 | } 71 | } 72 | ``` 73 | 74 | ``` 75 | 运行结果: 76 | Producer 生产: 1 77 | Consumer 消费: 1 78 | Producer 生产: 2 79 | Consumer 消费: 2 80 | Producer 生产: 3 81 | Consumer 消费: 3 82 | Producer 生产: 4 83 | Consumer 消费: 4 84 | Producer 生产: 5 85 | Consumer 消费: 5 86 | Producer 生产: 6 87 | Consumer 消费: 6 88 | Producer 生产: 7 89 | Consumer 消费: 7 90 | Producer 生产: 8 91 | Consumer 消费: 8 92 | Producer 生产: 9 93 | Consumer 消费: 9 94 | Producer 生产: 10 95 | Consumer 消费: 10 96 | ``` -------------------------------------------------------------------------------- /Chapter01/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter01/README.md -------------------------------------------------------------------------------- /Chapter01/ThreadSafe.md: -------------------------------------------------------------------------------- 1 | 集合与线程安全 2 | ============= 3 | 4 | 线程安全的集合包的三种实现方式。 5 | 6 | 摘抄:《Java 理论与实践: 并发集合类》 7 | 8 | 在Java类库中出现的第一个关联的集合类是 Hashtable ,它是JDK 1.0的一部分。 Hashtable 提供了一种易于使用的、线程安全的、关联的map功能,这当然也是方便的。然而,线程安全性是凭代价换来的―― Hashtable 的所有方法都是同步的。 此时,无竞争的同步会导致可观的性能代价。 9 | 10 | Hashtable 的后继者 HashMap 是作为JDK1.2中的集合框架的一部分出现的,它通过提供一个不同步的基类和一个同步的包装器 Collections.synchronizedMap ,解决了线程安全性问题。 通过将基本的功能从线程安全性中分离开来, Collections.synchronizedMap 允许需要同步的用户可以拥有同步,而不需要同步的用户则不必为同步付出代价。 11 | 12 | Hashtable 和 synchronizedMap 所采取的获得同步的简单方法(同步 Hashtable 中或者同步的 Map 包装器对象中的每个方法)有两个主要的不足。 13 | 14 | 首先,这种方法对于可伸缩性是一种障碍,因为一次只能有一个线程可以访问hash表。 15 | 16 | 同时,这样仍不足以提供真正的线程安全性,许多公用的混合操作仍然需要额外的同步。虽然诸如 get() 和 put() 之类的简单操作可以在不需要额外同步的情况下安全地完成,但还是有一些公用的操作序列 ,例如迭代或者put-if-absent(空则放入),需要外部的同步,以避免数据争用。 17 | 18 | 19 | #(1) 线程安全类 Vector,HashTable 20 | Vector,因为效率较低,现在已经不太建议使用。 21 | 22 | #(2) 使用ArrayList/HashMap和同步包装器 23 | 24 | List synchArrayList = Collections.synchronizedList(new ArrayList()); 25 | Map synchHashMap = Collections.synchronizedMap(new HashMap()) ; 26 | 27 | 如果要迭代,需要这样 28 | 29 | synchronized (synchHashMap) { 30 | Iterator iter = synchHashMap.keySet().iterator(); 31 | while (iter.hasNext()) . . .; 32 | } 33 | 34 | #(3) java.util.concurrent包 35 | 用java5.0新加入的ConcurrentHashMap、ConcurrentLinkedQueue、CopyOnWriteArray- List 和 CopyOnWriteArraySet ,对这些集合进行并发修改是安全的。 36 | 37 | ConcurrentHashMap是线程安全的HashMap实现。无论元素数量为多少,在线程数为10时, ConcurrentHashMap带来的性能提升并不是很明显。 但在线程数为50和100时,Concurrent- HashMap在添加和删除元素时带来了一倍左右的性能提升,在查找元素上更是带来了10倍左右的性能提升,并且随着线程数增长,ConcurrentHashMap性能并没有出现下降的现象。 38 | 39 | CopyOnWriteArrayList是一个线程安全、并且在读操作时无锁的ArrayList。:在读多写少的并发环境中,一般用 CopyOnWriteArrayList 类替代 ArrayList 。 40 | -------------------------------------------------------------------------------- /Chapter02/Example.md: -------------------------------------------------------------------------------- 1 | 示例 2 | ====== 3 | 4 | Java线程:新特征-原子量 5 | 6 | http://lavasoft.blog.51cto.com/62575/222541 7 | 8 | 反面例子(切勿模仿): 9 | 10 | ```java 11 | /** 12 | * Java线程:新特征-原子量 13 | */ 14 | public class Test { 15 | public static void main(String[] args) { 16 | ExecutorService pool = Executors.newFixedThreadPool(2); 17 | Runnable t1 = new MyRunnable("张三", 2000); 18 | Runnable t2 = new MyRunnable("李四", 3600); 19 | Runnable t3 = new MyRunnable("王五", 2700); 20 | Runnable t4 = new MyRunnable("老张", 600); 21 | Runnable t5 = new MyRunnable("老牛", 1300); 22 | Runnable t6 = new MyRunnable("胖子", 800); 23 | // 执行各个线程 24 | pool.execute(t1); 25 | pool.execute(t2); 26 | pool.execute(t3); 27 | pool.execute(t4); 28 | pool.execute(t5); 29 | pool.execute(t6); 30 | // 关闭线程池 31 | pool.shutdown(); 32 | } 33 | } 34 | 35 | class MyRunnable implements Runnable { 36 | // 原子量,每个线程都可以自由操作 37 | private static AtomicLong aLong = new AtomicLong(10000); 38 | private String name; // 操作人 39 | private int x; // 操作数额 40 | 41 | MyRunnable(String name, int x) { 42 | this.name = name; 43 | this.x = x; 44 | } 45 | 46 | public void run() { 47 | System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x)); 48 | } 49 | } 50 | ``` 51 | 52 | ``` 53 | 运行结果: 54 | 李四执行了3600,当前余额:13600 55 | 王五执行了2700,当前余额:16300 56 | 老张执行了600,当前余额:16900 57 | 老牛执行了1300,当前余额:18200 58 | 胖子执行了800,当前余额:19000 59 | 张三执行了2000,当前余额:21000 60 | 61 | 张三执行了2000,当前余额:12000 62 | 王五执行了2700,当前余额:18300 63 | 老张执行了600,当前余额:18900 64 | 老牛执行了1300,当前余额:20200 65 | 胖子执行了800,当前余额:21000 66 | 李四执行了3600,当前余额:15600 67 | 68 | 张三执行了2000,当前余额:12000 69 | 李四执行了3600,当前余额:15600 70 | 老张执行了600,当前余额:18900 71 | 老牛执行了1300,当前余额:20200 72 | 胖子执行了800,当前余额:21000 73 | 王五执行了2700,当前余额:18300 74 | ``` 75 | 76 | 从运行结果可以看出,虽然使用了原子量,但是程序并发访问还是有问题,那究竟问题出在哪里了? 77 | 78 | 这里要注意的一点是,原子量虽然可以保证单个变量在某一个操作过程的安全,但无法保证你整个代码块,或者整个程序的安全性。因此,通常还应该使用锁等同步机制来控制整个程序的安全性。 79 | 80 | 下面是对这个错误修正: 81 | 82 | ```java 83 | /** 84 | * Java线程:新特征-原子量 85 | */ 86 | public class Test { 87 | public static void main(String[] args) { 88 | ExecutorService pool = Executors.newFixedThreadPool(2); 89 | Lock lock = new ReentrantLock(false); 90 | Runnable t1 = new MyRunnable("张三", 2000, lock); 91 | Runnable t2 = new MyRunnable("李四", 3600, lock); 92 | Runnable t3 = new MyRunnable("王五", 2700, lock); 93 | Runnable t4 = new MyRunnable("老张", 600, lock); 94 | Runnable t5 = new MyRunnable("老牛", 1300, lock); 95 | Runnable t6 = new MyRunnable("胖子", 800, lock); 96 | // 执行各个线程 97 | pool.execute(t1); 98 | pool.execute(t2); 99 | pool.execute(t3); 100 | pool.execute(t4); 101 | pool.execute(t5); 102 | pool.execute(t6); 103 | // 关闭线程池 104 | pool.shutdown(); 105 | } 106 | } 107 | 108 | class MyRunnable implements Runnable { 109 | // 原子量,每个线程都可以自由操作 110 | private static AtomicLong aLong = new AtomicLong(10000); 111 | private String name; // 操作人 112 | private int x; // 操作数额 113 | private Lock lock; 114 | 115 | MyRunnable(String name, int x, Lock lock) { 116 | this.name = name; 117 | this.x = x; 118 | this.lock = lock; 119 | } 120 | 121 | public void run() { 122 | lock.lock(); 123 | System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x)); 124 | lock.unlock(); 125 | } 126 | } 127 | 128 | ``` 129 | 130 | ``` 131 | 执行结果: 132 | 张三执行了2000,当前余额:12000 133 | 王五执行了2700,当前余额:14700 134 | 老张执行了600,当前余额:15300 135 | 老牛执行了1300,当前余额:16600 136 | 胖子执行了800,当前余额:17400 137 | 李四执行了3600,当前余额:21000 138 | ``` 139 | 140 | 这里使用了一个对象锁,来控制对并发代码的访问。不管运行多少次,执行次序如何,最终余额均为21000,这个结果是正确的。 141 | -------------------------------------------------------------------------------- /Chapter02/HowToUse.md: -------------------------------------------------------------------------------- 1 | Atomic典型应用:按顺序获取ID 2 | ======= 3 | 4 | 传统方式必须在每次获取时加锁,以防止出现并发时取到同样id的现象。 5 | 6 | 用AtomicInteger实现示例如下: 7 | 8 | ```java 9 | private static AtomicInteger counter = new AtomicInteger(); 10 | public static int getNextId() { 11 | return counter.incrementAndGet(); 12 | } 13 | ``` -------------------------------------------------------------------------------- /Chapter02/README.md: -------------------------------------------------------------------------------- 1 | 原子操作类Atomic 2 | ======== 3 | 4 | 相关类有:AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference。 5 | 6 | 这些原子类的方法都是基于CPU的CAS原语来操作的。基于CAS的方式比使用synchronized的方式性能提高2.8倍。因此对于使用JDK5以上版本且必须支持高并发而言,应尽量使用atomic的类。 7 | -------------------------------------------------------------------------------- /Chapter03/CreateThreadPool.md: -------------------------------------------------------------------------------- 1 | Executors创建线程池 2 | ======= 3 | 4 | 类库提供了一个灵活的线程池以及一些有用的默认配置,可以通过调用Executors中的静态工厂方法之一创建一个线程池。 5 | 6 | // 创建一个可重用固定线程数的线程池 7 | ExecutorService pool = Executors.newFixedThreadPool(2); 8 | 9 | 10 | (1) newFixedThreadPool 11 | 12 | 创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。 13 | 14 | (2) newCachedThreadPool 15 | 16 | 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 17 | 18 | (3) newSingleThreadExecutor 19 | 20 | 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 21 | 22 | (4) newScheduledThreadPool 23 | 24 | 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 25 | 26 | (5) newFixedThreadPool,newCachedThreadPool返回通用的ThreadPoolExecutor实例。 27 | -------------------------------------------------------------------------------- /Chapter03/Example.md: -------------------------------------------------------------------------------- 1 | 线程池创建示例 2 | ========== 3 | #1. 固定大小的线程池 4 | 5 | ```java 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ExecutorService; 8 | 9 | /** 10 | * Java线程:线程池-固定线程数的线程池 11 | */ 12 | public class Test { 13 | public static void main(String[] args) { 14 | // 创建一个可重用固定线程数的线程池 15 | ExecutorService pool = Executors.newFixedThreadPool(2); 16 | // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 17 | Thread t1 = new MyThread(); 18 | Thread t2 = new MyThread(); 19 | Thread t3 = new MyThread(); 20 | Thread t4 = new MyThread(); 21 | Thread t5 = new MyThread(); 22 | // 将线程放入池中进行执行 23 | pool.execute(t1); 24 | pool.execute(t2); 25 | pool.execute(t3); 26 | pool.execute(t4); 27 | pool.execute(t5); 28 | // 关闭线程池 29 | pool.shutdown(); 30 | } 31 | } 32 | 33 | class MyThread extends Thread { 34 | @Override 35 | public void run() { 36 | System.out.println(Thread.currentThread().getName() + "正在执行。。。"); 37 | } 38 | } 39 | 40 | ``` 41 | 42 | ``` 43 | pool-1-thread-1正在执行。。。 44 | pool-1-thread-2正在执行。。。 45 | pool-1-thread-1正在执行。。。 46 | pool-1-thread-2正在执行。。。 47 | pool-1-thread-1正在执行。。。 48 | ``` 49 | 50 | #2. 单任务线程池 51 | 52 | 在上例的基础上改一行创建pool对象的代码为: 53 | ``` 54 | //创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 55 | ExecutorService pool = Executors.newSingleThreadExecutor(); 56 | 57 | 输出结果为: 58 | pool-1-thread-1正在执行。。。 59 | pool-1-thread-1正在执行。。。 60 | pool-1-thread-1正在执行。。。 61 | pool-1-thread-1正在执行。。。 62 | pool-1-thread-1正在执行。。。 63 | ``` 64 | 65 | #3. 可变尺寸的线程池 66 | 67 | 与上面的类似,只是改动下pool的创建方式: 68 | ``` 69 | //创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 70 | ExecutorService pool = Executors.newCachedThreadPool(); 71 | 72 | pool-1-thread-2正在执行。。。 73 | pool-1-thread-1正在执行。。。 74 | pool-1-thread-3正在执行。。。 75 | pool-1-thread-4正在执行。。。 76 | pool-1-thread-5正在执行。。。 77 | ``` 78 | 79 | #4. 延迟连接池 80 | 81 | ```java 82 | import java.util.concurrent.Executors; 83 | import java.util.concurrent.ScheduledExecutorService; 84 | import java.util.concurrent.TimeUnit; 85 | 86 | /** 87 | * Java线程:线程池 88 | */ 89 | public class Test { 90 | public static void main(String[] args) { 91 | //创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 92 | ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); 93 | //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 94 | Thread t1 = new MyThread(); 95 | Thread t2 = new MyThread(); 96 | Thread t3 = new MyThread(); 97 | Thread t4 = new MyThread(); 98 | Thread t5 = new MyThread(); 99 | //将线程放入池中进行执行 100 | pool.execute(t1); 101 | pool.execute(t2); 102 | pool.execute(t3); 103 | //使用延迟执行风格的方法 104 | pool.schedule(t4, 10, TimeUnit.MILLISECONDS); 105 | pool.schedule(t5, 10, TimeUnit.MILLISECONDS); 106 | //关闭线程池 107 | pool.shutdown(); 108 | } 109 | } 110 | 111 | class MyThread extends Thread { 112 | @Override 113 | public void run() { 114 | System.out.println(Thread.currentThread().getName() + "正在执行。。。"); 115 | } 116 | } 117 | 118 | ``` 119 | 120 | ``` 121 | pool-1-thread-2正在执行。。。 122 | pool-1-thread-1正在执行。。。 123 | pool-1-thread-2正在执行。。。 124 | pool-1-thread-1正在执行。。。 125 | pool-1-thread-2正在执行。。。 126 | ``` 127 | 128 | #5. 单任务延迟连接池 129 | 130 | 在四代码基础上,做改动 131 | 132 | ``` 133 | //创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 134 | ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); 135 | 136 | pool-1-thread-1正在执行。。。 137 | pool-1-thread-1正在执行。。。 138 | pool-1-thread-1正在执行。。。 139 | pool-1-thread-1正在执行。。。 140 | pool-1-thread-1正在执行。。。 141 | ``` 142 | 143 | #6. 自定义线程池 144 | 145 | ```java 146 | import java.util.concurrent.ArrayBlockingQueue; 147 | import java.util.concurrent.BlockingQueue; 148 | import java.util.concurrent.ThreadPoolExecutor; 149 | import java.util.concurrent.TimeUnit; 150 | 151 | /** 152 | * Java线程:线程池-自定义线程池 153 | */ 154 | public class Test { 155 | public static void main(String[] args) { 156 | // 创建等待队列 157 | BlockingQueue bqueue = new ArrayBlockingQueue(20); 158 | // 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 159 | ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 2, TimeUnit.MILLISECONDS, bqueue); 160 | // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 161 | Thread t1 = new MyThread(); 162 | Thread t2 = new MyThread(); 163 | Thread t3 = new MyThread(); 164 | Thread t4 = new MyThread(); 165 | Thread t5 = new MyThread(); 166 | Thread t6 = new MyThread(); 167 | Thread t7 = new MyThread(); 168 | // 将线程放入池中进行执行 169 | pool.execute(t1); 170 | pool.execute(t2); 171 | pool.execute(t3); 172 | pool.execute(t4); 173 | pool.execute(t5); 174 | pool.execute(t6); 175 | pool.execute(t7); 176 | // 关闭线程池 177 | pool.shutdown(); 178 | } 179 | } 180 | 181 | class MyThread extends Thread { 182 | @Override 183 | public void run() { 184 | System.out.println(Thread.currentThread().getName() + "正在执行。。。"); 185 | try { 186 | Thread.sleep(100L); 187 | } catch (InterruptedException e) { 188 | e.printStackTrace(); 189 | } 190 | } 191 | } 192 | ``` 193 | 194 | ``` 195 | pool-1-thread-1正在执行。。。 196 | pool-1-thread-2正在执行。。。 197 | pool-1-thread-1正在执行。。。 198 | pool-1-thread-2正在执行。。。 199 | pool-1-thread-1正在执行。。。 200 | pool-1-thread-2正在执行。。。 201 | pool-1-thread-2正在执行。。。 202 | ``` 203 | -------------------------------------------------------------------------------- /Chapter03/README.md: -------------------------------------------------------------------------------- 1 | 线程池Executor,ThreadPoolExecutor,Executors 2 | -------- 3 | 4 | #线程池介绍 5 | 6 | 线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。 7 | 8 | 任务是一组逻辑工作单元,而线程则是任务异步执行的机制。 9 | 10 | 线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个 ThreadPoolExecutor还维护着一些基本的统计数据,如完成的任务数。 11 | 12 | 线程池简化了线程的管理工作,并且java.util.concurrent提供了一种灵活的线程池实现作为Executor框架的一部分。在java类库中,任务执行的主要抽象不是Thread,而是Executor。 13 | 14 | public interface Executor { 15 | void execute(Runnable command); 16 | } 17 | 18 | Executor的实现还提供了对生命周期的支持,以及统计信息收集、应用程序管理机制和性能监视等机制。 -------------------------------------------------------------------------------- /Chapter03/TaskRun.md: -------------------------------------------------------------------------------- 1 | 任务执行的三种模式 2 | ============ 3 | 4 | #(1)串行的执行任务 5 | 6 | ```java 7 | //串行的web服务器 8 | class SingleThreadWebServer { 9 | public static void main(String[] args) throws IOException { 10 | ServerSocket socket = new ServerSocket(80); 11 | while (true) { 12 | Socket connection = socket.accept(); 13 | handleRequest(connection); 14 | } 15 | } 16 | } 17 | ``` 18 | SingleThreadWebServer很简单,且在理论上是正确的,但在实际生产环境中的执行性能却很糟糕,因为它每次只能处理一次请求。在服务器应用程序中,串行处理机制都无法提供高吞吐率或快速响应性。 19 | 20 | #(2)显式的为任务创建线程 21 | 22 | ```java 23 | //在web服务器中为每个请求启动一个新的线程(不要这么做) 24 | class ThreadPerTaskWebServer { 25 | public static void main(String[] args) throws IOException { 26 | ServerSocket socket = new ServerSocket(80); 27 | while (true) { 28 | final Socket connection = socket.accept(); 29 | Runnable task = new Runnable() { 30 | public void run() { 31 | handleRequest(connection); 32 | } 33 | }; 34 | new Thread(task).start(); 35 | } 36 | } 37 | } 38 | ``` 39 | 40 | 在正常的情况下,”为每个任务分配一个线程”的方法能提升串行执行任务的性能。只要请求的到达速率不超过服务器的请求处理能力,那么这种方法可以同时带来更快的响应性和更高的吞吐率。 41 | 42 | 但这种方法存在一些缺陷,尤其当需要创建大量线程的时候。 43 | * 线程生命周期的开销非常高。 44 | * 资源消耗。活跃的线程会消耗系统资源,尤其是内存。如果可运行的线程数多于可用处理器的数量,那么有些线程将闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量线程在竞争CPU资源时还将产生其他的性能开销。 45 | * 稳定性。在可创建线程的数量上存在一个限制。如果超过了这些限制,很可能会抛出outOfMemoryError异常,要想从这种错误中恢复过来是非常危险的,更简单的方法是通过构造程序来避免超出这些限制。 46 | 47 | 48 | #(3).基于线程池的实现 49 | 50 | ```java 51 | // 基于线程池的web服务器 52 | class TaskExecutionWebServer { 53 | private static final int NTHREADS = 100; 54 | private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); 55 | 56 | public static void main(String[] args) throws IOException { 57 | ServerSocket socket = new ServerSocket(80); 58 | while (true) { 59 | final Socket connection = socket.accept(); 60 | Runnable task = new Runnable() { 61 | public void run() { 62 | handleRequest(connection); 63 | } 64 | }; 65 | exec.execute(task); 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | 在TaskExecutionWebServer中,通过使用Executor,将请求处理任务的提交与任务的实际执行解耦开来,并且只需采用另一种不同的Executor实现,就可以改变服务器的行为。 72 | -------------------------------------------------------------------------------- /Chapter03/ThreadPoolExecutor.md: -------------------------------------------------------------------------------- 1 | ThreadPoolExecutor 2 | ======== 3 | 4 | #1.通用构造函数 5 | 6 | ``` 7 | public ThreadPoolExecutor(int corePoolSize, 8 | int maximumPoolSize, 9 | long keepAliveTime, 10 | TimeUnit unit, 11 | BlockingQueue workQueue, 12 | ThreadFactory threadFactory, 13 | RejectedExecutionHandler handler) 14 | ``` 15 | 16 | 用给定的初始参数和默认的线程工厂及处理程序创建新的 ThreadPoolExecutor。使用 Executors 工厂方法之一比使用此通用构造方法方便得多。 17 | 18 | 参数: 19 | 20 | corePoolSize - 池中所保存的线程数,包括空闲线程。 21 | 22 | maximumPoolSize - 池中允许的最大线程数。 23 | 24 | keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。 25 | 26 | unit - keepAliveTime 参数的时间单位。 27 | 28 | workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。 29 | 30 | threadFactory - 执行程序创建新线程时使用的工厂。 31 | 32 | handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。 33 | 34 | ###抛出: 35 | 36 | IllegalArgumentException - 如果 corePoolSize 或 keepAliveTime 小于零,或者 maximumPoolSize 小于或等于零,或者 corePoolSize 大于 maximumPoolSize。 37 | 38 | NullPointerException - 如果 workQueue、threadFactory 或 handler 为 null。 39 | 40 | #2. 线程的创建和销毁 41 | 线程池的基本大小(corePoolSize)、最大大小(maximumPoolSize)以及存活时间等因素共同负责线程的创建与销毁。 42 | 43 | 基本大小也是线程池的目标大小,即在没有任务执行时线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。 44 | 45 | 最大大小表示可同时活动的线程数量的上限。 46 | 47 | 如果某个线程的空闲时间超过了存活时间,那么将被标记为可回收的,并且当线程池的当前大小超过基本大小时,这个线程将被终止。 48 | 49 | #3. 管理队列任务 50 | 51 | ThreadPoolExecutor允许提供一个BlockingQueue来保存等待执行的任务。基本的任务排队方法有3种:有界队列, 无界队列, 同步移交(Synchronous Handoff)。 52 | 53 | #4. 有界队列饱和策略 54 | 55 | 有界队列被填满后,饱和策略开始发挥作用。饱和策略可以通过调用setRejectedExecutionHandler来修改。jdk提供了几种不同的RejectedExecutionHandler实现:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy 。 56 | 57 | AbortPolicy:默认的饱和策略。抛出 RejectedExecutionException。调用者可以捕获这个异常,然后根据需求编写自己的处理代码。 58 | 59 | CallerRunsPolicy: 不抛弃任务,不抛出异常,而将任务退回给调用者。 60 | 61 | DiscardOldestPolicy:放弃最旧的未处理请求,然后重试 execute;如果执行程序已关闭,则会丢弃该任务。 62 | 63 | DiscardPolicy:默认情况下它将放弃被拒绝的任务。 64 | 65 | #5. 扩展ThreadPoolExecutor 66 | 67 | ThreadPoolExecutor是可扩展的,它提供了几个可以在子类化中改写的方法:beforeExecute、afterExcute和terminated。在这些方法中,还可以添加日志、计时、监视或统计信息收集的功能。 68 | 69 | 无论任务是从run中正常返回,还是会抛出一个异常在而返回,afterExcute都会被调用。(如果任务执行完成后带有Error,那么就不会调用afterExcute)。 70 | 71 | 如果beforeExecute抛出一个RuntimeException,那么任务将不被执行,并且afterExcute也会被调用。 72 | 73 | 在线程池完成关闭操作时调用terminated,也就是在所有任务都已经完成并且所有工作者线程也已经关闭后,terminated可以释放Executor在其生命周期里分配的各种资源,此外还可以执行发送通知,记录日志或者收集finalize统计信息等操作。 74 | 75 | ```java 76 | //增加了日志和计时等功能的线程池 77 | public class TimingThreadPool extends ThreadPoolExecutor { 78 | 79 | private final ThreadLocal startTime = new ThreadLocal(); 80 | private final Logger log = Logger.getLogger("TimingThreadPool"); 81 | private final AtomicLong numTasks = new AtomicLong(); 82 | private final AtomicLong totalTime = new AtomicLong(); 83 | //构造函数 84 | //... 85 | 86 | protected void beforeExecute(Thread t, Runnable r) { 87 | super.beforeExecute(t, r); 88 | log.fine(String.format("Thread %s: start %s", t, r)); 89 | startTime.set(System.nanoTime()); 90 | } 91 | 92 | protected void afterExecute(Runnable r, Throwable t) { 93 | try { 94 | long endTime = System.nanoTime(); 95 | long taskTime = endTime - startTime.get(); 96 | numTasks.incrementAndGet(); 97 | totalTime.addAndGet(taskTime); 98 | log.fine(String.format("Thread %s: end %s, time=%dns", t, r, 99 | taskTime)); 100 | } finally { 101 | super.afterExecute(r, t); 102 | } 103 | } 104 | 105 | protected void terminated() { 106 | try { 107 | log.info(String.format("Terminated: avg time=%dns", totalTime.get() 108 | / numTasks.get())); 109 | } finally { 110 | super.terminated(); 111 | } 112 | } 113 | } 114 | ``` 115 | -------------------------------------------------------------------------------- /Chapter04/CallableFuture.md: -------------------------------------------------------------------------------- 1 | 携带结果的任务Callable与Future 2 | ========= 3 | 4 | Executor框架使用Runnable作为其任务的基本表达形式。Runnable是一个有很大局限的抽象,它不能返回一个值或抛出一个受检查的异常。 5 | 6 | Callable是更好的抽象:它认为主进入点(即call)将返回一个值,并可能抛出一个异常。 7 | 8 | Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。在Future规范中包含的隐含意义是,任务的生命周期只能前进,不能后退,当某个任务完成后,它将永远停留在“完成”的状态上。 9 | 10 | get方法的行为取决于任务的状态(尚未开始、正在运行、已完成)。 11 | 12 | 如果任务已经完成,那么get方法会立即返回或都抛出一个Exception。 13 | 14 | 如果任务没有完成,那么get将阻塞并直到任务完成。 15 | 16 | 如果任务抛出了异常,那么get将该异常封装为ExecutionException并重新抛出。如果get抛出ExecutionException,那么可以通过getCause获得被封装的初始异常。 17 | 18 | 如果任务被取消,那么get将抛出CanclelationException。 19 | 20 | java code:callable与Future接口 21 | 22 | ```java 23 | public interface Callable { 24 | V call() throws Exception; 25 | } 26 | public interface Future { 27 | boolean cancel(boolean mayInterruptIfRunning); 28 | boolean isCancelled(); 29 | boolean isDone(); 30 | V get() throws InterruptedException, ExecutionException, 31 | CancellationException; 32 | V get(long timeout, TimeUnit unit) 33 | throws InterruptedException, ExecutionException, 34 | CancellationException, TimeoutException; 35 | } 36 | ``` 37 | 38 | #实例 39 | 40 | 可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。 41 | 42 | 执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。 43 | 44 | ```java 45 | import java.util.concurrent.Callable; 46 | import java.util.concurrent.ExecutionException; 47 | import java.util.concurrent.ExecutorService; 48 | import java.util.concurrent.Executors; 49 | import java.util.concurrent.Future; 50 | 51 | /** 52 | * Java线程:有返回值的线程 53 | */ 54 | public class Test { 55 | public static void main(String[] args) throws ExecutionException, 56 | InterruptedException { 57 | // 创建一个线程池 58 | ExecutorService pool = Executors.newFixedThreadPool(2); 59 | // 创建两个有返回值的任务 60 | Callable c1 = new MyCallable("A"); 61 | Callable c2 = new MyCallable("B"); 62 | // 执行任务并获取Future对象 63 | Future f1 = pool.submit(c1); 64 | Future f2 = pool.submit(c2); 65 | // 从Future对象上获取任务的返回值,并输出到控制台 66 | System.out.println(">>>" + f1.get().toString()); 67 | System.out.println(">>>" + f2.get().toString()); 68 | // 关闭线程池 69 | pool.shutdown(); 70 | } 71 | } 72 | 73 | class MyCallable implements Callable { 74 | private String oid; 75 | 76 | MyCallable(String oid) { 77 | this.oid = oid; 78 | } 79 | 80 | public Object call() throws Exception { 81 | return oid + "任务返回的内容"; 82 | } 83 | } 84 | 85 | ``` 86 | 87 | ``` 88 | >>>A任务返回的内容 89 | >>>B任务返回的内容 90 | ``` 91 | -------------------------------------------------------------------------------- /Chapter04/README.md: -------------------------------------------------------------------------------- 1 | 任务异步返回结果和取消关闭 2 | ============ 3 | -------------------------------------------------------------------------------- /Chapter04/TaskCancle.md: -------------------------------------------------------------------------------- 1 | 任务取消(todo 中断策略 ...) 2 | =========== 3 | 4 | 如果外部代码能在某个操作正常完成之前将其置入"完成"状态,那么这个操作就可以称为可取消的(cancellable)。 5 | 6 | ###取消的原因有多种: 7 | 8 | (1). 用户请求取消。如通过管理接口来发送取消请求。 9 | 10 | (2). 有时间限制的操作。例如某个程序需要在有限时间内搜索问题空间,并在这个时间内选择最佳的解决方案。当计时器超时时,需要取消所有正在搜索的任务。 11 | 12 | (3). 应用程序事件。例如某个程序对某个问题空间进行分解并搜索,从而使不同的任务可以搜索问题空间中的不同区域。当其中一个任务找到了解决方案时,所有其它仍在搜索的任务都将被取消。 13 | 14 | (4). 错误。网页爬虫程序搜索相关的页面,并将页面或摘要数据保存到硬盘。当一个爬虫任务发生错误时(例如磁盘满),那么所有的搜索任务都会取消,此时可能会记录它们的当前状态,以便稍后重新启动。 15 | 16 | (5). 关闭。当一个程序或服务关闭时,必须对正在处理和等待处理的工作执行来某种操作。在平缓的关闭中,当前正在执行的任务将继续执行直到完成,而在立即关闭过程中,当前的任务可能取消。 17 | 18 | #1. 使用volatile类型的域来保存取消状态 19 | 20 | ```java 21 | public class PrimeGenerator implements Runnable { 22 | private final List primes = new ArrayList(); 23 | private volatile boolean cancelled; 24 | 25 | public void run() { 26 | BigInteger p = BigInteger.ONE; 27 | while (!cancelled) { 28 | p = p.nextProbablePrime(); 29 | synchronized (this) { 30 | primes.add(p); 31 | } 32 | } 33 | } 34 | 35 | public void cancel() { 36 | cancelled = true; 37 | } 38 | 39 | public synchronized List get() { 40 | return new ArrayList(primes); 41 | } 42 | } 43 | ``` 44 | 45 | #2. 通过中断来取消 46 | Thread中的中断方法 47 | 48 | ```java 49 | public class Thread { 50 | public void interrupt() { ... } 51 | public boolean isInterrupted() { ... } 52 | public static boolean interrupted() { ... } 53 | ... 54 | } 55 | 56 | class PrimeProducer extends Thread { 57 | private final BlockingQueue queue; 58 | PrimeProducer(BlockingQueue queue) { 59 | this.queue = queue; 60 | } 61 | public void run() { 62 | try { 63 | BigInteger p = BigInteger.ONE; 64 | while (!Thread.currentThread().isInterrupted()) 65 | queue.put(p = p.nextProbablePrime()); 66 | } catch (InterruptedException consumed) { 67 | /* Allow thread to exit */ 68 | } 69 | } 70 | public void cancel() { 71 | interrupt(); 72 | } 73 | } 74 | ``` 75 | 76 | #3. 通过Future来取消 77 | 78 | ```java 79 | public static void timedRun(Runnable r, 80 | long timeout, TimeUnit unit) 81 | throws InterruptedException { 82 | Future task = taskExec.submit(r); 83 | try { 84 | task.get(timeout, unit); 85 | } catch (TimeoutException e) { 86 | // 接下来任务将被取消 87 | } catch (ExecutionException e) { 88 | // 如果在任务中抛出了异常,那么重新抛出异常 89 | throw launderThrowable(e.getCause()); 90 | } 91 | finally { 92 | // 如果任务已经结束,那么取消也不会带来任何影响 93 | task.cancel(true); // 如果任务正在运行,那么将被终短 94 | } 95 | } 96 | ``` 97 | 98 | -------------------------------------------------------------------------------- /Chapter05/Lock-ReentrantLock.md: -------------------------------------------------------------------------------- 1 | Lock与ReentrantLock 2 | ========= 3 | 4 | Lock接口中定义了一组抽象的加锁机制。Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。 5 | 6 | ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。 7 | 8 | ```java 9 | public interface Lock { 10 | void lock(); 11 | //如果当前线程未被中断,则获取锁定。 12 | void lockInterruptibly() throws InterruptedException; 13 | boolean tryLock(); 14 | boolean tryLock(long timeout, TimeUnit unit) 15 | throws InterruptedException; 16 | void unlock(); 17 | Condition newCondition(); 18 | } 19 | ``` 20 | 21 | 使用ReentrantLock来保护对象状态。 22 | ```java 23 | Lock lock = new ReentrantLock(); 24 | lock.lock(); 25 | try { 26 | //相关操作 27 | } finally { 28 | lock.unlock(); //一定要释放锁 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /Chapter05/README.md: -------------------------------------------------------------------------------- 1 | 显式锁 Lock 2 | ======= 3 | 协调共享对象访问的机制:java5之前是synchronized和volatile(),java5增加了ReentrantLock。 4 | -------------------------------------------------------------------------------- /Chapter05/ReadWriteLock.md: -------------------------------------------------------------------------------- 1 | 读-写锁 2 | ====== 3 | 4 | ReadWriteLock 维护了一对相关的锁定,一个用于只读操作,另一个用于写入操作。 5 | 6 | ```java 7 | //ReadWriteLock 接口 8 | public interface ReadWriteLock { 9 | Lock readLock(); 10 | Lock writeLock(); 11 | } 12 | ``` 13 | 14 | 15 | 示例:用读-写锁来包装Map 16 | 17 | ```java 18 | public class ReadWriteMap { 19 | private final Map map; 20 | private final ReadWriteLock lock = new ReentrantReadWriteLock(); 21 | private final Lock r = lock.readLock(); 22 | private final Lock w = lock.writeLock(); 23 | public ReadWriteMap(Map map) { 24 | this.map = map; 25 | } 26 | public V put(K key, V value) { 27 | w.lock(); 28 | try { 29 | return map.put(key, value); 30 | } 31 | finally { 32 | w.unlock(); 33 | } 34 | } 35 | // 对 remove(), putAll(), clear()等方法执行相同的操作 36 | 37 | public V get(Object key) { 38 | r.lock(); 39 | try { 40 | return map.get(key); 41 | } 42 | finally { 43 | r.unlock(); 44 | } 45 | } 46 | // 对其它只读的方法执行相同的操作 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /Chapter05/lockInterruptibly.md: -------------------------------------------------------------------------------- 1 | 可中断的锁 2 | ======== 3 | 4 | ```java 5 | public boolean sendOnSharedLine(Stringmessage) 6 | throws InterruptedException{ 7 | lock.lockInterruptibly(); //如果当前线程未被中断,则获取锁定。 8 | try{ 9 | return cancellableSendOnSharedLine(message); 10 | }finally{ 11 | lock.unlock(); 12 | } 13 | } 14 | private boolean cancellableSendOnSharedLine(String message) 15 | throwsInterruptedException{...} 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /Chapter05/tryLock.md: -------------------------------------------------------------------------------- 1 | 2 | 轮询锁和定时锁 3 | ========= 4 | 5 | 可定时的与可轮询的锁获取模式是由tryLock方法实现的,与无条件的锁获取模式相比,它具有更完善的错误恢复机制。 6 | 7 | #1. 轮询锁 8 | 9 | ```java 10 | //示例:通过tryLock来避免锁顺序死锁。 11 | //利用tryLock来获取两个锁,如果不能同时获得,那么回退并重新尝试。如果在指定的时间内不能获得所需要的锁,那么tansferMoney将返回一个失败状态,从而使该操作平缓地失败。 12 | public boolean transferMoney(Account fromAcct, 13 | Account toAcct, 14 | DollarAmount amount, 15 | long timeout, 16 | TimeUnit unit) 17 | throws InsufficientFundsException, InterruptedException { 18 | long fixedDelay = getFixedDelayComponentNanos(timeout, unit); 19 | long randMod = getRandomDelayModulusNanos(timeout, unit); 20 | long stopTime = System.nanoTime() + unit.toNanos(timeout); 21 | while (true) { 22 | if (fromAcct.lock.tryLock()) { 23 | try { 24 | if (toAcct.lock.tryLock()) { 25 | try { 26 | if (fromAcct.getBalance().compareTo(amount) < 0) 27 | throw new InsufficientFundsException(); 28 | else { 29 | fromAcct.debit(amount); 30 | toAcct.credit(amount); 31 | return true; 32 | } 33 | } 34 | finally { 35 | toAcct.lock.unlock(); 36 | } 37 | } 38 | } 39 | finally { 40 | fromAcct.lock.unlock(); 41 | } 42 | } 43 | if (System.nanoTime() < stopTime) 44 | return false; 45 | NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod); 46 | } 47 | } 48 | 49 | ``` 50 | 51 | #2. 定时锁 52 | 53 | Java 5提供了更灵活的锁工具,可以显式地索取和释放锁。那么在索取锁的时候可以设定一个超时时间,如果超过这个时间还没索取到锁,则不会继续堵塞而是放弃此次任务,示例代码如下: 54 | 55 | ```java 56 | public boolean trySendOnSharedLine(String message, 57 | long timeout, TimeUnit unit) 58 | throws InterruptedException { 59 | long nanosToLock = unit.toNanos(timeout) 60 | - estimatedNanosToSend(message); 61 | if (!lock.tryLock(nanosToLock, NANOSECONDS)) 62 | return false; 63 | try { 64 | return sendOnSharedLine(message); 65 | } finally { 66 | lock.unlock(); 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /Chapter06/CyclicBarrier-CountDownLatch.md: -------------------------------------------------------------------------------- 1 | 栅栏CyclicBarrier与计数器CountDownLatch 2 | ========= 3 | 4 | #1. 栅栏 CyclicBarrier 5 | 6 | 栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。所有线程必须同时到达栅栏位置,才能继续执行。栅栏用于实现一些协议,例如几个家庭成员决定在某个地方集合:”所有人6:00在麦当劳集合,到了以后要等其他人,之后再讨论下一步要做的事”。 7 | 8 | CyclicBarrier可以使一定数量的参与方反复地在栅栏位置汇集,它在并行迭代算法中非常有用。 9 | 10 | 在模拟程序中通常需要栅栏,例如某个步骤中的计算可以并行执行,但必须等到该步骤中的所有计算都执行完毕才能进入下一个步骤。 11 | 12 | ```java 13 | ExecutorService service = Executors.newCachedThreadPool(); 14 | // 构造方法里的数字标识有几个线程到达集合地点开始进行下一步工作 15 | final CyclicBarrier cb = new CyclicBarrier(3); 16 | for (int i = 0; i < 3; i++) { 17 | Runnable runnable = new Runnable() { 18 | public void run() { 19 | try { 20 | Thread.sleep((long) (Math.random() * 10000)); 21 | System.out.println("线程" 22 | + Thread.currentThread().getName() 23 | + "即将到达集合地点1,当前已有" + cb.getNumberWaiting() 24 | + "个已经到达,正在等候"); 25 | cb.await(); 26 | 27 | Thread.sleep((long) (Math.random() * 10000)); 28 | System.out.println("线程" 29 | + Thread.currentThread().getName() 30 | + "即将到达集合地点2,当前已有" + cb.getNumberWaiting() 31 | + "个已经到达,正在等候"); 32 | cb.await(); 33 | Thread.sleep((long) (Math.random() * 10000)); 34 | System.out.println("线程" 35 | + Thread.currentThread().getName() 36 | + "即将到达集合地点3,当前已有" + cb.getNumberWaiting() 37 | + "个已经到达,正在等候"); 38 | cb.await(); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | }; 44 | service.execute(runnable); 45 | 46 | } 47 | service.shutdown(); 48 | ``` 49 | 50 | ``` 51 | 执行结果: 52 | 线程pool-1-thread-2即将到达集合地点1,当前已有0个已经到达,正在等候 53 | 线程pool-1-thread-1即将到达集合地点1,当前已有1个已经到达,正在等候 54 | 线程pool-1-thread-3即将到达集合地点1,当前已有2个已经到达,正在等候 55 | 线程pool-1-thread-1即将到达集合地点2,当前已有0个已经到达,正在等候 56 | 线程pool-1-thread-3即将到达集合地点2,当前已有1个已经到达,正在等候 57 | 线程pool-1-thread-2即将到达集合地点2,当前已有2个已经到达,正在等候 58 | 线程pool-1-thread-1即将到达集合地点3,当前已有0个已经到达,正在等候 59 | 线程pool-1-thread-2即将到达集合地点3,当前已有1个已经到达,正在等候 60 | 线程pool-1-thread-3即将到达集合地点3,当前已有2个已经到达,正在等候 61 | ``` 62 | 63 | #2. 计数器 CountDownLatch 64 | 65 | ```java 66 | ExecutorService service = Executors.newCachedThreadPool(); 67 | final CountDownLatch cdOrder = new CountDownLatch(1); 68 | final CountDownLatch cdAnswer = new CountDownLatch(3); 69 | for (int i = 0; i < 3; i++) { 70 | Runnable runnable = new Runnable() { 71 | public void run() { 72 | try { 73 | System.out.println("线程" 74 | + Thread.currentThread().getName() + "正准备接受命令"); 75 | cdOrder.await(); 76 | System.out.println("线程" 77 | + Thread.currentThread().getName() + "已接受命令"); 78 | Thread.sleep((long) (Math.random() * 10000)); 79 | System.out 80 | .println("线程" 81 | + Thread.currentThread().getName() 82 | + "回应命令处理结果"); 83 | cdAnswer.countDown(); 84 | } catch (Exception e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | }; 89 | service.execute(runnable); 90 | } 91 | try { 92 | Thread.sleep((long) (Math.random() * 10000)); 93 | 94 | System.out.println("线程" + Thread.currentThread().getName() 95 | + "即将发布命令"); 96 | cdOrder.countDown(); 97 | System.out.println("线程" + Thread.currentThread().getName() 98 | + "已发送命令,正在等待结果"); 99 | cdAnswer.await(); 100 | System.out.println("线程" + Thread.currentThread().getName() 101 | + "已收到所有响应结果"); 102 | } catch (Exception e) { 103 | e.printStackTrace(); 104 | } 105 | service.shutdown(); 106 | ``` 107 | 108 | ``` 109 | 结果: 110 | 线程pool-1-thread-1正准备接受命令 111 | 线程pool-1-thread-2正准备接受命令 112 | 线程pool-1-thread-3正准备接受命令 113 | 线程main即将发布命令 114 | 线程main已发送命令,正在等待结果 115 | 线程pool-1-thread-1已接受命令 116 | 线程pool-1-thread-2已接受命令 117 | 线程pool-1-thread-3已接受命令 118 | 线程pool-1-thread-1回应命令处理结果 119 | 线程pool-1-thread-2回应命令处理结果 120 | 线程pool-1-thread-3回应命令处理结果 121 | 线程main已收到所有响应结果 122 | ``` 123 | -------------------------------------------------------------------------------- /Chapter06/README.md: -------------------------------------------------------------------------------- 1 | 同步 2 | ===== 3 | -------------------------------------------------------------------------------- /Chapter06/Semaphore.md: -------------------------------------------------------------------------------- 1 | 信号量Semaphore 2 | ======== 3 | 计数信号量(Counting Semaphore)用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。 4 | 5 | Semaphore中管理着一组虚拟的许可(permit),许可的初始数量可通过构造函数来指定。在执行操作时可以首先获得许可(只要还有剩余的许可),并在使用以后释放许可。如果没有许可,那么acquire将阻塞直到许可(或者被中断或者超时)。release方法将返回一个许可信号量。 6 | 7 | ```java 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.Semaphore; 11 | 12 | public class SemaphoreTest { 13 | public static void main(String[] args) { 14 | MyPool myPool = new MyPool(20); 15 | // 创建线程池 16 | ExecutorService threadPool = Executors.newFixedThreadPool(2); 17 | MyThread t1 = new MyThread("任务A", myPool, 3); 18 | MyThread t2 = new MyThread("任务B", myPool, 12); 19 | MyThread t3 = new MyThread("任务C", myPool, 7); 20 | // 在线程池中执行任务 21 | threadPool.execute(t1); 22 | threadPool.execute(t2); 23 | threadPool.execute(t3); 24 | // 关闭池 25 | threadPool.shutdown(); 26 | } 27 | } 28 | 29 | /** 30 | * 一个池 31 | */ 32 | class MyPool { 33 | private Semaphore sp; // 池相关的信号量 34 | 35 | /** 36 | * 池的大小,这个大小会传递给信号量 37 | * 38 | * @param size 39 | * 池的大小 40 | */ 41 | MyPool(int size) { 42 | this.sp = new Semaphore(size); 43 | } 44 | 45 | public Semaphore getSp() { 46 | return sp; 47 | } 48 | 49 | public void setSp(Semaphore sp) { 50 | this.sp = sp; 51 | } 52 | } 53 | 54 | class MyThread extends Thread { 55 | private String threadname; // 线程的名称 56 | private MyPool pool; // 自定义池 57 | private int x; // 申请信号量的大小 58 | 59 | MyThread(String threadname, MyPool pool, int x) { 60 | this.threadname = threadname; 61 | this.pool = pool; 62 | this.x = x; 63 | } 64 | 65 | public void run() { 66 | try { 67 | // 从此信号量获取给定数目的许可 68 | pool.getSp().acquire(x); 69 | // todo:也许这里可以做更复杂的业务 70 | System.out.println(threadname + "成功获取了" + x + "个许可!"); 71 | } catch (InterruptedException e) { 72 | e.printStackTrace(); 73 | } finally { 74 | // 释放给定数目的许可,将其返回到信号量。 75 | pool.getSp().release(x); 76 | System.out.println(threadname + "释放了" + x + "个许可!"); 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | ``` 83 | 结果: 84 | 任务A成功获取了3个许可! 85 | 任务B成功获取了12个许可! 86 | 任务A释放了3个许可! 87 | 任务B释放了12个许可! 88 | 任务C成功获取了7个许可! 89 | 任务C释放了7个许可! 90 | ``` 91 | -------------------------------------------------------------------------------- /Chapter07/Example.md: -------------------------------------------------------------------------------- 1 | 例子 2 | ====== 3 | 4 | ```java 5 | public class SeqGen { 6 | private static ThreadLocal seqNum = new ThreadLocal() { 7 | @Override 8 | public Integer initialValue() { 9 | return 0; 10 | } 11 | }; 12 | 13 | public int getNextNum() { 14 | seqNum.set(seqNum.get() + 1); 15 | return seqNum.get(); 16 | } 17 | } 18 | 19 | public class TestThread implements Runnable { 20 | private SeqGen sn; 21 | 22 | public TestThread(SeqGen sn) { 23 | this.sn = sn; 24 | } 25 | 26 | public void run() { 27 | for (int i = 0; i < 3; i++) { 28 | String str = Thread.currentThread().getName() + "-->" 29 | + sn.getNextNum(); 30 | System.out.println(str); 31 | } 32 | } 33 | } 34 | 35 | public class Test { 36 | public static void main(String[] args) { 37 | SeqGen sn = new SeqGen(); 38 | TestThread tt1 = new TestThread(sn); 39 | TestThread tt2 = new TestThread(sn); 40 | TestThread tt3 = new TestThread(sn); 41 | Thread t1 = new Thread(tt1); 42 | Thread t2 = new Thread(tt2); 43 | Thread t3 = new Thread(tt3); 44 | t1.start(); 45 | t2.start(); 46 | t3.start(); 47 | } 48 | } 49 | ``` 50 | 51 | ``` 52 | 输出结果: 53 | Thread-2-->1 54 | Thread-2-->2 55 | Thread-2-->3 56 | Thread-1-->1 57 | Thread-0-->1 58 | Thread-0-->2 59 | Thread-0-->3 60 | Thread-1-->2 61 | Thread-1-->3 62 | ``` 63 | 64 | 资料来源: 65 | 66 | (1) 理解ThreadLocal 67 | http://blog.csdn.net/qjyong/article/details/2158097 68 | 69 | (2) ThreadLocal的几种误区 70 | http://www.blogjava.net/jspark/archive/2006/08/01/61165.html 71 | 72 | (3) 正确理解ThreadLocal 73 | http://lujh99.iteye.com/blog/103804 74 | -------------------------------------------------------------------------------- /Chapter07/README.md: -------------------------------------------------------------------------------- 1 | ThreadLocal 2 | ========= 3 | #1.介绍 4 | 5 | ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。ThreadLocal功能非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。 6 | 7 | 从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。 8 | 9 | 通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。 10 | 11 | ThreadLocal的应用场合,我觉得最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。 12 | 13 | ThreadLocal的接口方法: 14 | T get() 15 | 返回此线程局部变量的当前线程副本中的值。 16 | 17 | protected T initialValue() 18 | 返回此线程局部变量的当前线程的“初始值”。 19 | 20 | void remove() 21 | 移除此线程局部变量当前线程的值。 22 | 23 | void set(T value) 24 | 将此线程局部变量的当前线程副本中的值设置为指定值。 25 | 26 | 27 | #2.ThreadLocal的几种误区 28 | 29 | 最近由于需要用到ThreadLocal,在网上搜索了一些相关资料,发现对ThreadLocal经常会有下面几种误解: 30 | 31 | (1) ThreadLocal是java线程的一个实现 32 | ThreadLocal的确是和java线程有关,不过它并不是java线程的一个实现,它只是用来维护本地变量。针对每个线程,提供自己的变量版本,主要是为了避免线程冲突,每个线程维护自己的版本。彼此独立,修改不会影响到对方。 33 | 34 | (2) ThreadLocal是相对于每个session的 35 | 36 | ThreadLocal顾名思义,是针对线程。在java web编程上,每个用户从开始到会话结束,都有自己的一个session标识。但是ThreadLocal并不是在会话层上。其实,Threadlocal是独立于用户session的。它是一种服务器端行为,当服务器每生成一个新的线程时,就会维护自己的ThreadLocal。对于这个误解,个人认为应该是开发人员在本地基于一些应用服务器测试的结果。众所周知,一般的应用服务器都会维护一套线程池,也就是说,对于每次访问,并不一定就新生成一个线程。而是自己有一个线程缓存池。对于访问,先从缓存池里面找到已有的线程,如果已经用光,才去新生成新的线程。所以,由于开发人员自己在测试时,一般只有他自己在测,这样服务器的负担很小,这样导致每次访问可能是共用同样一个线程,导致会有这样的误解:每个session有一个ThreadLocal 37 | 38 | (3) ThreadLocal是相对于每个线程的,用户每次访问会有新的ThreadLocal 39 | 40 | 理论上来说,ThreadLocal是的确是相对于每个线程,每个线程会有自己的ThreadLocal。但是上面已经讲到,一般的应用服务器都会维护一套线程池。因此,不同用户访问,可能会接受到同样的线程。因此,在做基于TheadLocal时,需要谨慎,避免出现ThreadLocal变量的缓存,导致其他线程访问到本线程变量 41 | 42 | (4) 对每个用户访问,ThreadLocal可以多用 43 | 44 | 可以说,ThreadLocal是一把双刃剑,用得来的话可以起到非常好的效果。但是,ThreadLocal如果用得不好,就会跟全局变量一样。代码不能重用,不能独立测试。因为,一些本来可以重用的类,现在依赖于ThreadLocal变量。如果在其他没有ThreadLocal场合,这些类就变得不可用了。 45 | 46 | 个人觉得ThreadLocal用得很好的几个应用场合,值得参考 47 | * 存放当前session用户:quake want的jert 48 | * 存放一些context变量,比如webwork的ActionContext 49 | * 存放session,比如Spring hibernate orm的session 50 | -------------------------------------------------------------------------------- /Chapter07/jdk1.5ScourceCode.md: -------------------------------------------------------------------------------- 1 | 下面来看看ThreadLocal的实现原理(jdk1.5源码) 2 | ========= 3 | ```java 4 | public class ThreadLocal { 5 | /** 6 | * ThreadLocals rely on per-thread hash maps attached to each thread 7 | * (Thread.threadLocals and inheritableThreadLocals). The ThreadLocal 8 | * objects act as keys, searched via threadLocalHashCode. This is a 9 | * custom hash code (useful only within ThreadLocalMaps) that eliminates 10 | * collisions in the common case where consecutively constructed 11 | * ThreadLocals are used by the same threads, while remaining well-behaved 12 | * in less common cases. 13 | */ 14 | private final int threadLocalHashCode = nextHashCode(); 15 | 16 | /** 17 | * The next hash code to be given out. Accessed only by like-named method. 18 | */ 19 | private static int nextHashCode = 0; 20 | 21 | /** 22 | * The difference between successively generated hash codes - turns 23 | * implicit sequential thread-local IDs into near-optimally spread 24 | * multiplicative hash values for power-of-two-sized tables. 25 | */ 26 | private static final int HASH_INCREMENT = 0x61c88647; 27 | 28 | /** 29 | * Compute the next hash code. The static synchronization used here 30 | * should not be a performance bottleneck. When ThreadLocals are 31 | * generated in different threads at a fast enough rate to regularly 32 | * contend on this lock, memory contention is by far a more serious 33 | * problem than lock contention. 34 | */ 35 | private static synchronized int nextHashCode() { 36 | int h = nextHashCode; 37 | nextHashCode = h + HASH_INCREMENT; 38 | return h; 39 | } 40 | 41 | /** 42 | * Creates a thread local variable. 43 | */ 44 | public ThreadLocal() { 45 | } 46 | 47 | /** 48 | * Returns the value in the current thread's copy of this thread-local 49 | * variable. Creates and initializes the copy if this is the first time 50 | * the thread has called this method. 51 | * 52 | * @return the current thread's value of this thread-local 53 | */ 54 | public T get() { 55 | Thread t = Thread.currentThread(); 56 | ThreadLocalMap map = getMap(t); 57 | if (map != null) 58 | return (T)map.get(this); 59 | 60 | // Maps are constructed lazily. if the map for this thread 61 | // doesn't exist, create it, with this ThreadLocal and its 62 | // initial value as its only entry. 63 | T value = initialValue(); 64 | createMap(t, value); 65 | return value; 66 | } 67 | 68 | /** 69 | * Sets the current thread's copy of this thread-local variable 70 | * to the specified value. Many applications will have no need for 71 | * this functionality, relying solely on the {@link #initialValue} 72 | * method to set the values of thread-locals. 73 | * 74 | * @param value the value to be stored in the current threads' copy of 75 | * this thread-local. 76 | */ 77 | public void set(T value) { 78 | Thread t = Thread.currentThread(); 79 | ThreadLocalMap map = getMap(t); 80 | if (map != null) 81 | map.set(this, value); 82 | else 83 | createMap(t, value); 84 | } 85 | 86 | /** 87 | * Get the map associated with a ThreadLocal. Overridden in 88 | * InheritableThreadLocal. 89 | * 90 | * @param t the current thread 91 | * @return the map 92 | */ 93 | ThreadLocalMap getMap(Thread t) { 94 | return t.threadLocals; 95 | } 96 | 97 | /** 98 | * Create the map associated with a ThreadLocal. Overridden in 99 | * InheritableThreadLocal. 100 | * 101 | * @param t the current thread 102 | * @param firstValue value for the initial entry of the map 103 | * @param map the map to store. 104 | */ 105 | void createMap(Thread t, T firstValue) { 106 | t.threadLocals = new ThreadLocalMap(this, firstValue); 107 | } 108 | 109 | ....... 110 | 111 | /** 112 | * ThreadLocalMap is a customized hash map suitable only for 113 | * maintaining thread local values. No operations are exported 114 | * outside of the ThreadLocal class. The class is package private to 115 | * allow declaration of fields in class Thread. To help deal with 116 | * very large and long-lived usages, the hash table entries use 117 | * WeakReferences for keys. However, since reference queues are not 118 | * used, stale entries are guaranteed to be removed only when 119 | * the table starts running out of space. 120 | */ 121 | static class ThreadLocalMap { 122 | 123 | ........ 124 | 125 | } 126 | 127 | } 128 | ``` 129 | 130 | 可以看到ThreadLocal类中的变量只有这3个int型: 131 | 132 | private final int threadLocalHashCode = nextHashCode(); 133 | private static int nextHashCode = 0; 134 | private static final int HASH_INCREMENT = 0x61c88647; 135 | 136 | 而作为ThreadLocal实例的变量只有 threadLocalHashCode 这一个,nextHashCode 和HASH_INCREMENT 是ThreadLocal类的静态变量,实际上HASH_INCREMENT是一个常量,表示了连续分配的两个ThreadLocal实例的threadLocalHashCode值的增量,而nextHashCode 的表示了即将分配的下一个ThreadLocal实例的threadLocalHashCode 的值。 137 | 138 | 可以来看一下创建一个ThreadLocal实例即new ThreadLocal()时做了哪些操作,从上面看到构造函数ThreadLocal()里什么操作都没有,唯一的操作是这句: 139 | 140 | private final int threadLocalHashCode = nextHashCode(); 141 | 142 | 那么nextHashCode()做了什么呢: 143 | 144 | ```java 145 | private static synchronized int nextHashCode() { 146 | int h = nextHashCode; 147 | nextHashCode = h + HASH_INCREMENT; 148 | return h; 149 | } 150 | ``` 151 | 152 | 就是将ThreadLocal类的下一个hashCode值即nextHashCode的值赋给实例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT这个值。 153 | 154 | 因此ThreadLocal实例的变量只有这个threadLocalHashCode,而且是final的,用来区分不同的ThreadLocal实例,ThreadLocal类主要是作为工具类来使用,那么ThreadLocal.set()进去的对象是放在哪儿的呢? 155 | 156 | 看一下上面的set()方法,两句合并一下成为 157 | 158 | ThreadLocalMap map = Thread.currentThread().threadLocals; 159 | 160 | 这个ThreadLocalMap 类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中: 161 | 162 | ```java 163 | 1. public class Thread implements Runnable { 164 | 2. ...... 165 | 3. 166 | 4. /* ThreadLocal values pertaining to this thread. This map is maintained 167 | 5. * by the ThreadLocal class. */ 168 | 6. ThreadLocal.ThreadLocalMap threadLocals = null; 169 | 7. ...... 170 | 8. } 171 | ``` 172 | 173 | 再看这句: 174 | ``` 175 | if (map != null) 176 | map.set(this, value); 177 | ``` 178 | 也就是将该ThreadLocal实例作为key,要保持的对象作为值,设置到当前线程的ThreadLocalMap 中,get()方法同样大家看了代码也就明白了,ThreadLocalMap 类的代码太多了,我就不帖了,自己去看源码吧。 179 | -------------------------------------------------------------------------------- /Chapter08/Future.md: -------------------------------------------------------------------------------- 1 | Future模式 2 | ============= 3 | 4 | 某一段程序提交了一个请求,期望得到一个答复。在传统单线程环境下,必须等服务程序返回结果后,才能进行其它处理。而在Future模式中,调用方式改为异步,在主调用函数中,原来等待返回结果的时间段,可以处理其它事务。 5 | 6 | Future模式的主要参与者 7 | 8 | 参与者 | 作用 9 | --- | --- 10 | Main | 系统调用,调用Client发出请求 11 | Client | 返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData 12 | Data | 返回数据的接口 13 | FutureDate | Future数据,构造很快,但是是一个虚拟的数据,需要装配RealData 14 | RealData | 真实数据,但是构造较慢 15 | 16 | 17 | 18 | #Future模式代码实现 19 | 20 | ###(1) Data.java 21 | 22 | ```java 23 | public interface Data { 24 | public String getResult(); 25 | } 26 | ``` 27 | 28 | ###(2) RealData.java 29 | 30 | ```java 31 | public class RealData implements Data { 32 | protected final String result; 33 | public RealData(String para) { 34 | //RealData的构造可能很慢,需要用户等待很久 35 | StringBuffer sb=new StringBuffer(); 36 | for (int i = 0; i < 10; i++) { 37 | sb.append(para); 38 | try { 39 | Thread.sleep(100); 40 | } catch (InterruptedException e) { 41 | } 42 | } 43 | result=sb.toString(); 44 | } 45 | public String getResult() { 46 | return result; 47 | } 48 | } 49 | ``` 50 | 51 | ###(3) FutureData.java 52 | 53 | ```java 54 | public class FutureData implements Data { 55 | protected RealData realdata = null; 56 | protected boolean isReady = false; 57 | public synchronized void setRealData(RealData realdata) { 58 | if (isReady) { 59 | return; 60 | } 61 | this.realdata = realdata; 62 | isReady = true; 63 | notifyAll(); 64 | } 65 | public synchronized String getResult() { 66 | while (!isReady) { 67 | try { 68 | wait(); 69 | } catch (InterruptedException e) { 70 | } 71 | } 72 | return realdata.result; 73 | } 74 | } 75 | ``` 76 | 77 | ###(4) Client.java 78 | ``` 79 | public class Client { 80 | public Data request(final String queryStr) { 81 | final FutureData future = new FutureData(); 82 | // RealData的构建很慢 83 | new Thread() { 84 | public void run() { 85 | RealData realdata = new RealData(queryStr); 86 | future.setRealData(realdata); 87 | } 88 | }.start(); 89 | return future; 90 | } 91 | } 92 | ``` 93 | 94 | ###(5) Main.java 95 | ```java 96 | public class Main { 97 | public static void main(String[] args) { 98 | Client client = new Client(); 99 | 100 | Data data = client.request("a"); 101 | System.out.println("请求完毕"); 102 | try { 103 | //这里可以用一个sleep代替了对其它业务逻辑的处理 104 | Thread.sleep(2000); 105 | } catch (InterruptedException e) { 106 | } 107 | //使用真实的数据 108 | System.out.println("数据 = " + data.getResult()); 109 | } 110 | } 111 | ``` 112 | 113 | ``` 114 | 输出结果: 115 | 请求完毕 116 | 数据 = aaaaaaaaaa 117 | ``` 118 | 119 | #JDK实现 120 | ![jdk future](./img/img01.png) 121 | 122 | ###(1) RealData.java 123 | 124 | ```java 125 | import java.util.concurrent.Callable; 126 | 127 | public class RealData implements Callable { 128 | private String para; 129 | public RealData(String para){ 130 | this.para=para; 131 | } 132 | @Override 133 | public String call() throws Exception { 134 | 135 | StringBuffer sb=new StringBuffer(); 136 | for (int i = 0; i < 10; i++) { 137 | sb.append(para); 138 | try { 139 | Thread.sleep(100); 140 | } catch (InterruptedException e) { 141 | } 142 | } 143 | return sb.toString(); 144 | } 145 | } 146 | ``` 147 | 148 | ###(2) Main.java 149 | 150 | ```java 151 | import java.util.concurrent.ExecutionException; 152 | import java.util.concurrent.ExecutorService; 153 | import java.util.concurrent.Executors; 154 | import java.util.concurrent.FutureTask; 155 | 156 | public class Main { 157 | public static void main(String[] args) throws InterruptedException, ExecutionException { 158 | //构造FutureTask 159 | FutureTask future = new FutureTask(new RealData("a")); 160 | ExecutorService executor = Executors.newFixedThreadPool(1); 161 | //执行FutureTask,相当于上例中的 client.request("a") 发送请求 162 | //在这里开启线程进行RealData的call()执行 163 | executor.submit(future); 164 | System.out.println("请求完毕"); 165 | try { 166 | //这里依然可以做额外的数据操作,这里使用sleep代替其他业务逻辑的处理 167 | Thread.sleep(2000); 168 | } catch (InterruptedException e) { 169 | } 170 | //相当于上例中得data.getContent(),取得call()方法的返回值 171 | //如果此时call()方法没有执行完成,则依然会等待 172 | System.out.println("数据 = " + future.get()); 173 | } 174 | } 175 | ``` 176 | 177 | ``` 178 | 输出结果: 179 | 请求完毕 180 | 数据 = aaaaaaaaaa 181 | ``` 182 | -------------------------------------------------------------------------------- /Chapter08/GuardedSuspension.md: -------------------------------------------------------------------------------- 1 | Guarded-Suspension模式 2 | ===== 3 | 4 | Guarded-Suspension意为保护暂停,其核心思想是仅当服务进程准备好,才提供服务。设 想一种场景,服务器可能会在很短时间内承受大量的客户端请求,客户端请求的数量可能超过服务器本身的即时处理能力,而服务器程序又不能丢弃任何一个客户请求。此时,最佳的处理方案莫过于让客户端请求进行排队,由服务端程序一个接一个处理。这样既保征了所有的客户端请求均不丢失,同时也避免了服务器由于同时处理太多的请求而崩溃。 5 | 6 | (1) Guarded-Suspension结构 7 | 8 | Guarded-Suspension模式的主要角色 9 | 10 | 角色 | 作用 11 | --- | --- 12 | Request | 表示客户端请求 13 | RequestQueue | 用于保存客户端请求队列 14 | ClientThread | 客户端进程 15 | ServerThread | 服务端进程 16 | 17 | ClientThread负责不断的发起请求,并将请求对象放入请求队列。ServerThread则根据其自身的状态,在有能力处理请求时,从RequestQueue中提取请求对象加以处理,系统的工作流程如图: 18 | 19 | ![jdk future](./img/img03.png) 20 | 21 | #代码实现 22 | ###(1) Request.java 23 | ```java 24 | public class Request { 25 | private String name; 26 | public Request(String name) { 27 | this.name = name; 28 | } 29 | public String getName() { 30 | return name; 31 | } 32 | public String toString() { 33 | return "[ Request " + name + " ]"; 34 | } 35 | } 36 | ``` 37 | 38 | ###(2) RequestQueue.java 39 | 40 | ```java 41 | import java.util.LinkedList; 42 | 43 | public class RequestQueue { 44 | private LinkedList queue = new LinkedList(); 45 | 46 | public synchronized Request getRequest() { 47 | while (queue.size() == 0) { 48 | try { 49 | wait(); 50 | } catch (InterruptedException e) { 51 | } 52 | } 53 | return (Request) queue.remove(); 54 | } 55 | 56 | public synchronized void addRequest(Request request) { 57 | queue.add(request); 58 | notifyAll(); // 通知getRequest()方法 59 | } 60 | } 61 | ``` 62 | 63 | ###(3) ClientThread.java 64 | 65 | ```java 66 | public class ClientThread extends Thread { 67 | private RequestQueue requestQueue; 68 | public ClientThread(RequestQueue requestQueue, String name) { 69 | super(name); 70 | this.requestQueue = requestQueue; 71 | } 72 | public void run() { 73 | for (int i = 0; i < 10; i++) { 74 | Request request = new Request("RequestID:" + i+" Thread_Name:"+Thread.currentThread().getName()); 75 | System.out.println(Thread.currentThread().getName() + " requests " + request); 76 | requestQueue.addRequest(request); 77 | try { 78 | Thread.sleep(10); 79 | } catch (InterruptedException e) { 80 | } 81 | System.out.println("ClientThread Name is:"+Thread.currentThread().getName()); 82 | } 83 | System.out.println(Thread.currentThread().getName()+" request end"); 84 | } 85 | } 86 | ``` 87 | ###(4) ServerThread.java 88 | ```java 89 | public class ServerThread extends Thread { 90 | private RequestQueue requestQueue; 91 | public ServerThread(RequestQueue requestQueue, String name) { 92 | super(name); 93 | this.requestQueue = requestQueue; 94 | } 95 | public void run() { 96 | while (true) { 97 | final Request request = requestQueue.getRequest(); 98 | try { 99 | Thread.sleep(100); 100 | } catch (InterruptedException e) { 101 | e.printStackTrace(); 102 | } 103 | System.out.println(Thread.currentThread().getName() + " handles " + request); 104 | 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | ###(5) Main.java 111 | ```java 112 | public class Main { 113 | public static void main(String[] args) { 114 | RequestQueue requestQueue = new RequestQueue(); 115 | for (int i = 0; i < 10; i++) { 116 | new ServerThread(requestQueue, "ServerThread" + i).start(); 117 | } 118 | for (int i = 0; i < 10; i++) { 119 | new ClientThread(requestQueue, "ClientThread" + i).start(); 120 | } 121 | } 122 | } 123 | ``` 124 | 125 | ``` 126 | 执行结果: 127 | ClientThread4 requests [ Request RequestID:0 Thread_Name:ClientThread4 ] 128 | …省略… 129 | ClientThread Name is:ClientThread1 130 | ClientThread Name is:ClientThread4 131 | ClientThread Name is:ClientThread2 132 | ClientThread4 requests [ Request RequestID:1 Thread_Name:ClientThread4 ] 133 | ClientThread1 requests [ Request RequestID:1 Thread_Name:ClientThread1 ] 134 | ClientThread2 requests [ Request RequestID:1 Thread_Name:ClientThread2 ] 135 | …省略… 136 | ServerThread1 handles [ Request RequestID:0 Thread_Name:ClientThread8 ] 137 | ClientThread Name is:ClientThread3 138 | ClientThread3 request end 139 | ServerThread3 handles [ Request RequestID:0 Thread_Name:ClientThread3 ] 140 | ServerThread7 handles [ Request RequestID:0 Thread_Name:ClientThread2 ] 141 | ClientThread Name is:ClientThread5 142 | ClientThread Name is:ClientThread1 143 | ClientThread1 request end 144 | ClientThread Name is:ClientThread6 145 | ServerThread0 handles [ Request RequestID:0 Thread_Name:ClientThread5 ] 146 | ClientThread6 request end 147 | ClientThread5 request end 148 | ServerThread8 handles [ Request RequestID:1 Thread_Name:ClientThread1 ] 149 | ServerThread9 handles [ Request RequestID:1 Thread_Name:ClientThread4 ] 150 | …省略… 151 | ServerThread6 handles [ Request RequestID:9 Thread_Name:ClientThread3 ] 152 | ``` -------------------------------------------------------------------------------- /Chapter08/Immutable.md: -------------------------------------------------------------------------------- 1 | 不变模式immutable 2 | ====== 3 | 4 | 当对象产生后,不发生任何改变。 5 | 6 | 适合场景: 7 | 8 | (1) 当对象被创建后,其内部状态和数据不再发生变化。 9 | 10 | (2) 对象需要共享、被多线程频繁访问。 11 | 12 | ```java 13 | public final class Product { 14 | private final String no; 15 | private final String name; 16 | private final double price; 17 | 18 | public Product(String no, String name, double price) { 19 | super(); 20 | this.no = no; 21 | this.name = name; 22 | this.price = price; 23 | } 24 | 25 | public String getNo() { 26 | return no; 27 | } 28 | public String getName() { 29 | return name; 30 | } 31 | public double getPrice() { 32 | return price; 33 | } 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /Chapter08/MasterWorker.md: -------------------------------------------------------------------------------- 1 | Master-Worker模式 2 | ===== 3 | 4 | Master-Worker模式是常用的并行模式。它的核心思想是,系统由两类进程协作工作:Master进程,Worker进程。Master进程负责接收和分配任务,worker进程负责处理子任务。当各个Worker进程将子任务处理完成后,将结果返回给Master进程,由Master进程做归纳和汇总,从而得到系统的最终结果。 5 | 6 | ![](./img/img02.png) 7 | 8 | 9 | #代码实现 10 | 11 | 应用Master-Worker框架,实现计算立方和的应用,并计算1~100的立方和。 12 | 13 | 计算任务被分解为100个任务,每个任务仅用于计算单独的立方和。Master产生固定个数的worker来处理所有这些子任务。Worker不断地从任务集体集合中取得这些计算立方和的子任务,并将计算结果返回给Master.Master负责将所有worker累加,从而产生最终的结果。在计算过程中,Master和Worker的运行也是完全异步的,Master不必等所有的Worker都执行完成后,就可以进行求和操作。Master在获得部分子任务结果集时,就可以开始对最终结果进行计算,从而进一步提高系统的并行度和吞吐量。 14 | 15 | ###(1) Master.java 16 | 17 | ```java 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.Queue; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | import java.util.concurrent.ConcurrentLinkedQueue; 23 | 24 | public class Master { 25 | // 任务队列 26 | protected Queue workQueue = new ConcurrentLinkedQueue(); 27 | // Worker线程队列 28 | protected Map threadMap = new HashMap(); 29 | // 子任务处理结果集 30 | protected Map resultMap = new ConcurrentHashMap(); 31 | 32 | // 是否所有的子任务都结束了 33 | public boolean isComplete() { 34 | for (Map.Entry entry : threadMap.entrySet()) { 35 | if (entry.getValue().getState() != Thread.State.TERMINATED) { 36 | return false; 37 | } 38 | } 39 | return true; 40 | } 41 | 42 | // Master的构造,需要一个Worker进程逻辑,和需要的Worker进程数量 43 | public Master(Worker worker, int countWorker) { 44 | worker.setWorkQueue(workQueue); 45 | worker.setResultMap(resultMap); 46 | for (int i = 0; i < countWorker; i++) 47 | threadMap.put(Integer.toString(i), 48 | new Thread(worker, Integer.toString(i))); 49 | } 50 | 51 | // 提交一个任务 52 | public void submit(Object job) { 53 | workQueue.add(job); 54 | } 55 | 56 | // 返回子任务结果集 57 | public Map getResultMap() { 58 | return resultMap; 59 | } 60 | 61 | // 开始运行所有的Worker进程,进行处理 62 | public void execute() { 63 | for (Map.Entry entry : threadMap.entrySet()) { 64 | entry.getValue().start(); 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | 71 | ###(2) Worker.java 72 | 73 | ```java 74 | import java.util.Map; 75 | import java.util.Queue; 76 | 77 | public class Worker implements Runnable { 78 | // 任务队列,用于取得子任务 79 | protected Queue workQueue; 80 | // 子任务处理结果集 81 | protected Map resultMap; 82 | 83 | public void setWorkQueue(Queue workQueue) { 84 | this.workQueue = workQueue; 85 | } 86 | 87 | public void setResultMap(Map resultMap) { 88 | this.resultMap = resultMap; 89 | } 90 | 91 | // 子任务处理的逻辑,在子类中实现具体逻辑 92 | public Object handle(Object input) { 93 | return input; 94 | } 95 | 96 | @Override 97 | public void run() { 98 | while (true) { 99 | // 获取子任务 100 | Object input = workQueue.poll(); 101 | if (input == null) 102 | break; 103 | // 处理子任务 104 | Object re = handle(input); 105 | // 将处理结果写入结果集 106 | resultMap.put(Integer.toString(input.hashCode()), re); 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | 113 | ###(3) TestMasterWorker.java 114 | 115 | ```java 116 | import java.util.Map; 117 | import java.util.Set; 118 | 119 | import org.junit.Test; 120 | 121 | public class TestMasterWorker { 122 | 123 | public class PlusWorker extends Worker { 124 | public Object handle(Object input) { 125 | Integer i = (Integer) input; 126 | return i * i * i; 127 | } 128 | } 129 | 130 | @Test 131 | public void testMasterWorker() { 132 | Master m = new Master(new PlusWorker(), 5); 133 | for (int i = 0; i < 100; i++) { 134 | m.submit(i); 135 | } 136 | m.execute(); 137 | int re = 0; 138 | Map resultMap = m.getResultMap(); 139 | while (resultMap.size() > 0 || !m.isComplete()) { 140 | Set keys = resultMap.keySet(); 141 | String key = null; 142 | for (String k : keys) { 143 | key = k; 144 | break; 145 | } 146 | Integer i = null; 147 | if (key != null) { 148 | i = (Integer) resultMap.get(key); 149 | } 150 | if (i != null) { 151 | re += i; 152 | } 153 | if (key != null) { 154 | resultMap.remove(key); 155 | } 156 | } 157 | 158 | System.out.println("testMasterWorker:" + re); 159 | } 160 | 161 | @Test 162 | public void testPlus() { 163 | int re = 0; 164 | for (int i = 0; i < 100; i++) { 165 | re += i * i * i; 166 | } 167 | System.out.println("testPlus:" + re); 168 | } 169 | 170 | } 171 | ``` 172 | 173 | ``` 174 | 执行输出结果: 175 | testMasterWorker:24502500 176 | testPlus:24502500 177 | ``` 178 | -------------------------------------------------------------------------------- /Chapter08/Producter-Consumer.md: -------------------------------------------------------------------------------- 1 | 请参考:基于阻塞队列BlockingQueue的生产者、消费者模式 -------------------------------------------------------------------------------- /Chapter08/README.md: -------------------------------------------------------------------------------- 1 | 并行程序设计模式 2 | ===== -------------------------------------------------------------------------------- /Chapter08/img/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter08/img/img01.png -------------------------------------------------------------------------------- /Chapter08/img/img02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter08/img/img02.png -------------------------------------------------------------------------------- /Chapter08/img/img03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter08/img/img03.png -------------------------------------------------------------------------------- /Chapter09/Condition.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/Condition.md -------------------------------------------------------------------------------- /Chapter09/README.md: -------------------------------------------------------------------------------- 1 | 并发控制方法 2 | ======= 3 | 4 | -------------------------------------------------------------------------------- /Chapter09/ReadWriteLock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/ReadWriteLock.md -------------------------------------------------------------------------------- /Chapter09/ReentranLock.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/ReentranLock.md -------------------------------------------------------------------------------- /Chapter09/Semaphore.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/Semaphore.md -------------------------------------------------------------------------------- /Chapter09/ThreadLocal.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/ThreadLocal.md -------------------------------------------------------------------------------- /Chapter09/synchronized.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/synchronized.md -------------------------------------------------------------------------------- /Chapter09/volatile.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter09/volatile.md -------------------------------------------------------------------------------- /Chapter10/README.md: -------------------------------------------------------------------------------- 1 | 并发框架Amino 2 | ==== -------------------------------------------------------------------------------- /Chapter11/README.md: -------------------------------------------------------------------------------- 1 | 参考资料 2 | ================ 3 | (1) 《Java并发编程实战》 (美)Brian Goetz等 著 童云兰等 译 4 | 5 | (2) java5线程 Callable与Future的应用 6 | http://blog.csdn.net/itm_hadf/article/details/7479274 7 | 8 | (3) Java线程:大总结 9 | http://lavasoft.blog.51cto.com/62575/222742 10 | 11 | (4) CyclicBarrier与CountDownLatch、栅栏与计数器 12 | http://www.iteye.com/topic/713053 13 | 14 | (5) 《Java程序性能优化》 葛一鸣 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter12/README.md: -------------------------------------------------------------------------------- 1 | 4. 线程状态 2 | 3 | ![线程状态](./img/img1.png) 4 | 5 | (1) NEW:线程对象已经创建,还没有在其上调用start()方法。 6 | 7 | (2) RUNNABLE:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。 8 | 9 | (3) RUNNING:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。 10 | 11 | (4) 等待/阻塞/睡眠状态(WAIT/ BLOCKED/SLEEP):线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到RUNNABLE状态。 12 | 13 | (5) DEAD(TERMINATED ?):当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。 14 | -------------------------------------------------------------------------------- /Chapter12/img/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quxionglie/notes-learning-java-concurrency/656d003c23d0cd9bc64148ce77a99db7422e4239/Chapter12/img/img1.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java并发编程学习笔记 2 | ====== 3 | 4 | 这是我两年前(2012-05)的学习笔记。[点击直接访问web版页面](http://www.quxionglie.com/notes-learning-java-concurrency/)。 5 | 6 | -- 7 | 本文不会详细介绍java5以前的多线程开发的知识,而是重点介绍java5以来多线程开发的一些新变化。部分文字、代码都是摘抄、提炼过来的,大家有兴趣可查看(8.相关资料)中的提供的原材料。 8 | 9 | 本文的主要内容如下: 10 | 11 | 2.集合类。主要介绍多线程开发中如何使用集合(三种方法)。建议大家关注一下java.util. concurrent包中的相关类。 12 | 13 | 4.线程池。你需要明确的是: 任务只是一组逻辑工作单元,而线程则是任务异步执行的机制。任务与任务的执行是相分离的。 14 | 15 | 5.任务异步返回结果和取消关闭。以前的线程实现都是没有返回值的,但java5中,有返回值的线程是如何实现的呢? 16 | 17 | 6.显式锁。协调共享对象访问,在java5以前用synchronized实现,现在可以用Lock显式的lock()和unlock(),并且有定时锁,读写锁等,你用过吗? 18 | 19 | 7.栅栏CyclicBarrier、计数器CountDownLatch、信号量Semaphore。这几个类确实让人眼前一亮。你知道它们是做什么的吗? 20 | 21 | 看完本文,你可以放心的遵循《Effective Java中文版(第2版)》的并发实践了: 22 | >第68条:executor和task优先于线程 23 | >第69条:并发工具优先于wait和notify 24 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [集合类](Chapter01/README.md) 4 | * [常用的集合结构](Chapter01/CommonSet.md) 5 | * [集合与线程安全](Chapter01/ThreadSafe.md) 6 | * [队列:Queue, BlockingQueue](Chapter01/Queue.md) 7 | * [并发Deque](Chapter01/Deque.md) 8 | * [DelayQueue](Chapter01/DelayQueue.md) 9 | * [更多阅读](Chapter01/More.md) 10 | * [原子操作类Atomic](Chapter02/README.md) 11 | * [典型应用:按顺序获取ID](Chapter02/HowToUse.md) 12 | * [示例](Chapter02/Example.md) 13 | * [线程池Executor,ThreadPoolExecutor,Executors](Chapter03/README.md) 14 | * [任务执行的三种模式](Chapter03/TaskRun.md) 15 | * [ThreadPoolExecutor](Chapter03/ThreadPoolExecutor.md) 16 | * [Executors创建线程池](Chapter03/CreateThreadPool.md) 17 | * [线程池创建示例](Chapter03/Example.md) 18 | * [任务异步返回结果和取消关闭](Chapter04/README.md) 19 | * [Callable与Future](Chapter04/CallableFuture.md) 20 | * [任务取消](Chapter04/TaskCancle.md) 21 | * [显式锁](Chapter05/README.md) 22 | * [Lock与ReentrantLock](Chapter05/Lock-ReentrantLock.md) 23 | * [轮询锁和定时锁](Chapter05/tryLock.md) 24 | * [可中断的锁](Chapter05/lockInterruptibly.md) 25 | * [读-写锁](Chapter05/ReadWriteLock.md) 26 | * [同步](Chapter06/README.md) 27 | * [栅栏CyclicBarrier与计数器CountDownLatch](Chapter06/CyclicBarrier-CountDownLatch.md) 28 | * [信号量Semaphore](Chapter06/Semaphore.md) 29 | * [ThreadLocal](Chapter07/README.md) 30 | * [jdk1.5源码](Chapter07/jdk1.5ScourceCode.md) 31 | * [例子](Chapter07/Example.md) 32 | * [并行程序设计模式](Chapter08/README.md) 33 | * [Future模式](Chapter08/Future.md) 34 | * [Master-Worker模式](Chapter08/MasterWorker.md) 35 | * [Guarded-Suspension模式](Chapter08/GuardedSuspension.md) 36 | * [不变模式immutable](Chapter08/Immutable.md) 37 | * [生产者-消费者模式](Chapter08/Producter-Consumer.md) 38 | * [并发控制方法](Chapter09/README.md) 39 | * [Java内存模型与volatile ](Chapter09/volatile.md) 40 | * [同步关键字synchronized](Chapter09/synchronized.md) 41 | * [ReentranLock重入锁](Chapter09/ReentranLock.md) 42 | * [ReadWriteLock读写锁](Chapter09/ReadWriteLock.md) 43 | * [Condition对象](Chapter09/Condition.md) 44 | * [Semaphore信号量](Chapter09/Semaphore.md) 45 | * [ThreadLocal线程局部变量](Chapter09/ThreadLocal.md) 46 | * [并发框架Amino](Chapter10/README.md) 47 | * [参考资料](Chapter11/README.md) 48 | * [其它-线程状态](Chapter12/README.md) 49 | --------------------------------------------------------------------------------