Java并发学习笔记


线程:
设置线程名称方便排错,对线程中断作出恰当的响应。

线程副本:
ThreadLocal它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本。


线程资源同步机制:
JVM把对于working memory的操作分为了use、assign、load、store、lock、unlock,对于main memory的操作分为了read、write、lock、unlock(有线程执行引擎发出)。

 

lock/unlock机制原理与synchronized相同(一个Lock可以对应多个Condition,而synchronized把 Lock和Condition合并了,一个synchronized Lock只对应一个Condition), 都用于保证某段代码执行的原子性。由于锁会阻塞其他线程同样需要的锁的部分执行,所以使用锁机制时要避免死锁。
注意:  静态方法上加synchronized, 为同步整个类对象。

 

ReentrantLock

基于AbstractQueuedSynchronizer的实现,AbstractQueuedSynchronizer为基于整数状态值实现资 源的并发控制访问提供了很好的支持。

ReentrantLock(boolean fair)创建一个FairSync或NonfairSync的对象实例,NonfairSync或FairSync继承自内部Sync,而Sync继承 自AbstractQueuedSynchronizer。

lock()调用创建的sync对象实例的lock(),会调用LockSupport.park(this), 实际使用的是Unsafe.getUnsafe().park(false, 0L)。

unlock()调用创建的sync对象实例的release(int arg),会调用LockSupport.unpark(s.thread),实际使用的是Unsafe.getUnsafe().unpark(thread)。

 

ReentrantReadWriteLock

没有继承于ReentrantLock,提供读锁、写锁。基于AbstractQueuedSynchronizer来实现,自行实现判断是否可获取读锁或写锁。
读锁调用lock方法时,如果没有线程持有写锁,就可获得读锁而无需阻塞等待。
写锁调用lock方法时,如果没有线程持有读锁或写锁,就可获得写锁而无需阻塞等待,否则就需要阻塞等待,因此写操作影响整体性能。
读写锁分离,在读多写少的场景下可大幅度提升性能。

ReentrantReadWriteLock())创建ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock、FairSync或NonfairSync这三个的对象实例。都是静态内部类。

 

volatile的机制仅用于控制线程中对象的可见性(不会将其从main memory复制到work memory中,而是直接在main memory中进行操作), 不能保证操作的原子性。

atomic操作类(基于CAS的数据结构),无阻塞的、CAS由硬件提供原子操作指令实现的(CPU原语)。

操作都基于compareAndSet(int expect, int update),compareAndSet调用UnSafe的compareAndSwapXXX,因方法为native、基于CPU的CAS原语来实现。

CAS简单地说就是由CPU比较内存位置上的值是否为当前值,如是则设置为next,否则返回false,因此CAS代码片段要在一个无限循环中执行,这样可保证并发的健壮性。(LockFree算法)



线程交互机制:
JVM提供基于Object的wait/notify/notifyAll方式。
jdk 5.0提供的并发包:Condition的await/signal/singalAll、Semphore的acquire/release、CountDownLatch的await/countDown、CyclicBarrier的await。

CountDownLatch:用于控制多个线程同时开始(当你启动了一个线程,你需要等它执行结束,或当你启动很多线程,你需要这些线程等到通知后才真正开始)。


CyclicBarrier:如传入Runnable的对象。await达到了设定数量后,会首先执行此Runnable的对象,才继续往下执行,(来实现并发性能测试的聚合点)。

基于ReentrantLock、Condition。

 

Semaphore:用于控制某资源同时被访问的个数。

ReentrantLock(boolean fair)创建一个FairSync或NonfairSync的对象实例,NonfairSync或FairSync继承自内部Sync,而Sync继承 自AbstractQueuedSynchronizer。

tryAcquire(long timeout, TimeUnit unit)调用创建的sync对象实例的tryAcquireSharedNanos(int arg, long nanosTimeout)。

release()调用创建的sync对象实例的releaseShared(int arg)。

 

 

线程池:

ThreadPoolExecutor提供线程池服务。

构造函数:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

Future<?> submit(task),task封装为FutureTask, 再通过execute(Runnable)负责将封装后的task放入线程中执行。

ThreadPoolExecutor的4种RejectedExecutionHandle实现:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。

高效地支持并发的ThreadPoolExecutor,必须合理分配corePoolSize、maximumPoolSize、workQueue、handler。

以下工作队列:

高性能(任务缓冲队列大小为1,直接交给线程执行):SynchronousQueue。
缓冲执行(任务缓冲队列大小为corePoolSize):ArrayBlockingQueue、LinkedBlockingQueue。

 


任务的提交与执行:
为了方便并发执行任务,Executor是执行任务的实现。

Executors是Executor的工厂类。

newFixedThreadPool(int),corePoolSize数量且线程启动后一直运行(与maximumPoolSize数量一样),keepAliveTime为0,执行的task超出传入的大小值后,将放入工作队列LinkedBlockingQueue中,当执行的task超出工作队列大小时抛出RejectedExecutionException。

newSingleThreadExecutor(),corePoolSize与maximumPoolSize都为1的newFixedThreadPool。

newCachedThreadPool(),corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,keepAliveTime为60L秒(启动线程存活时间),工作队列SynchronousQueue, 在使用时执行的task会复用线程或启动新线程来执行。

newScheduledThreadPool(int),为ScheduledThreadPoolExecutor,corePoolSize为传入参数,maximumPoolSize为Integer.MAX_VALUE,keepAliveTime为0纳秒,工作队列DelayedWorkQueue。

 

Future是提交者和执行者之间的通讯实现,包括get(阻塞至任务完成),cancel,get(timeout)(等待一段时间)等。
Future也用于异步变同步的场景。可用于异步获取执行结果或取消执行任务的场景,通过传入Runnable与Callable给FutrueTask。

FutrueTask(task)

FutrueTask构造函数,创建一个内部类Sync的对象实例。Sync基于AbstractQueuedSynchronizer来实现,Sync(Callable<V> callable),如task是Runnable,需通过RunnableAdapter适配成Callable实现类。

run()调用Sync对象的innerRun方法,首先基于CAS将状态READY设置为RUNNING,如果设置失败则直接返回。

获取当前线程, 判断当前state是否为RUNNING,如果是则callable.call(),并把使用set保存执行结果,set调用的是Sync对象的innerSet(使用lock-free算法),否则就调用releaseShared(0)。

get(long timeout, TimeUnit unit)调用创建的sync对象实例的innerGet(long nanosTimeout)方法。

cancel(boolean mayInterruptIfRunning)调用创建的sync对象实例的innerCancel(boolean mayInterruptIfRunning)。

 


任务定时:
使用ScheduledExecutorService,不建议你再使用java.util.Timer。

可用于异步操作时需要超时回调的场景。

Timer与ScheduledThreadPoolExecutor的三大区别:

1)Timer只能单线程,一旦task执行缓慢,就会导致其他task执行推迟,ScheduledThreadPoolExecutor可执行控制线程数量。

2)当Timer抛出RuntimeException异常时,会导致Timer中所有task都不再执行。

3)ScheduledThreadPoolExecutor可执行Callable的task,从而在执行完毕后得到执行结果。



并发三大定律:Amdahl、Gustafson、Sun-Ni

 

 

并发集合分析:

1。ConcurrentHashMap

采用Segment对象数组,Segment继承ReetrantLock, Segment采用HashEntry对象数组(采用key、hash、next、value的HashEntry<K,V>对象)、线程安全。

多了一个concurrencyLevel属性,以及Segment具有threshold、loadFactor属性,指定大小为cap的HashEntry对象数组 。(threshold = (int)(newTable.length * loadFactor))

基于concurrencyLevel划分出了多个Segment来对于key-value进行存储。(默认分为16段,分别持有各自的锁,锁仅用于put和remove等改变集合对象的操作,基于volite及HashEntry链表的不变性实现读取的不加锁)

基于key hash寻找Segment对象存放到数组的位置,从而避免每次操作都得锁住整个数组。

put操作根据key hash找到对应数组中Segment对象,可能要对找到后的Segment对象中的HashEntry对象数组进行扩容(rehash方法,计算公式为oldCapacity<<1), 设置threshold值, 最后需要对扩大容量后对象数组重新hash并复制对象到新的数组中(p.hash & sizeMask),最后设置threshold值。

keySet.iterator()后, 通过遍历每个分段中的HashEntry对象数组,完成集合中所有对象的遍历,在遍历过程中也是不加锁的, 因此不会抛出ConcurrentModificationException。

size操作,由于没有一个全局锁,所以这样的方法比较复杂。在不加锁的情况下遍历所有的Segment,读取Segment的count, modCount, 完毕后再遍历所有的Segment,看modCount是否有改变, 如有改变则再尝试一次以上动作。如果还有问题,则遍历Segment加锁、然后遍历读取Segment的count,遍历Segment释放锁。

 

2。CopyOnWriteArrayList

基于保证增加与删除元素的互斥,读操作无锁、保证了很高的读性能但可能会出现读脏数据、线程安全、。

构造函数与ArrayList不同, 创建一个大小为0的数组。

add(E)使用ReentrantLock保证线程安全,每次都创建一个新的Object数组,此数组大小为当前数组大小加1,并将之前数组的内容复制到新数组中,新增加的对象放入数组末尾,最后把引用切换到新数组。

remove(E)

get(int)直接获取当前数组对应位置的元素, 没有加锁保护, 因此可能会出现读脏数据。

iterator()创建一个新的COWIterator对象实例,并保存一个当前数组的快照。

 

3。CopyOnWriteArraySet

基于CopyOnWriteArrayList,不同的是使用addIfAbsent、addAllAbsent,需遍历当前Object数组, 如当前Object数组已有了当前元素,则直接返回,否则就放入Object数组尾部并返回。

 

4。ArrayBlockingQueue

基于固定大小数组、ReentrantLock以及Condtion实现的阻塞先进先出队列(可指定时间的阻塞读写)、线程安全。

ArrayBlockingQueue(int capacity, boolean fair),没有默认构造,capacity为数组大小,fair用于初始化锁,两个锁Condition、notEmpty、notFull。(上述属性都为final)

offer(E e, long timeout, TimeUnit unit),用于将元素插入数组尾部,如数组已满则等待,直到以下三种情况才继续:被唤醒、到达指定时间、当前线程中断。(首先将指定时间转化为纳秒,然后加锁,如数组未满则将元素插入数组尾部,如已满且已超过指定时间则返回false,如未超时调用notFull的awaitNanos进行等待,线程被中断则抛出InterruptedException异常,如为被唤醒或超时则继续重复以上动作--lockfree算法)

poll(long timeout, TimeUnit unit), notEmpty的awaitNanos进行等待。

 

4。LinkedBlockingQueue

基于链表、读只在队头(take和poll)、写只在队尾(put和offer),因此采用两把锁,避免了读写竞争。在高并发读写都多的情况下性能比ArrayBlockingQueue好很多, 遍历及删除元素则锁住两把锁。

 

 

并发小结:

高性能并发类库:基于CAS、 读写锁分离、 拆分锁粒度、volatile、AbstractQueuedSynchronizer实现, 来尽量减少高并发时的竞争。

LockFree算法,不需要加锁。通常都是三个部分组成:循环、CAS (CompareAndSet)、回退。(可以使用LockFree算法,来实现乐观锁)

 

学习参考资料:

《分布式Java应用:基础与实践》、《Java并发编程实践》、温少的《Java并发程序设计教程》、The Java™ Tutorials、jdk7类库源码。

 

 

补充了《分布式Java应用:基础与实践》一些代码样例:

 

AtomicDemo源码

/** * */ package jdk.concurrent; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * atomic操作类(基于CAS的数据结构),CAS由硬件提供原子操作指令实现的(CPU原语)。 * CAS方式耗时为156毫秒, synchronized方式耗时为313毫秒。 * * @author yangwm Aug 12, 2010 4:45:06 PM */ public class AtomicDemo { private static int id = 0; private static AtomicInteger atomicId = new AtomicInteger(); private static CountDownLatch latch = null; public synchronized static int getNextId() { return ++id; } public static int getNextIdWithAtomic() { return atomicId.incrementAndGet(); } static class Task implements Runnable { private boolean isAtomic; public Task(boolean isAtomic) { this.isAtomic = isAtomic; } @Override public void run() { if (isAtomic) { for (int i = 0; i < 1000; i++) { getNextIdWithAtomic(); } } else { for (int i = 0; i < 1000; i++) { getNextId(); } } latch.countDown(); } } public static void main(String[] args) throws InterruptedException { latch = new CountDownLatch(50); long beginTime = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { new Thread(new Task(false)).start(); } latch.await(); System.out.println("Synchronized style consume time:" + (System.currentTimeMillis() - beginTime) + " ms"); latch = new CountDownLatch(50); beginTime = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { new Thread(new Task(true)).start(); } latch.await(); System.out.println(" CAS style consume time:" + (System.currentTimeMillis() - beginTime) + " ms"); } }

 

 

 

FutureTaskDemo源码

/** * */ package jdk.concurrent; import java.sql.Connection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.locks.ReentrantLock; import util.ConnectionUtil; /** * ConcurrentHashMap + FutureTask使得create Connection这种耗时操作并行化(异步获取执行结果)。 * HashMap + lock耗时为187毫秒, ConcurrentHashMap + FutureTask耗时为3172毫秒。 * * @author yangwm Aug 12, 2010 5:56:41 PM */ public class FutureTaskDemo { static Map<String, Connection> connectionPool = new HashMap<String, Connection>(); static ReentrantLock lock = new ReentrantLock(); static CountDownLatch latch = null; /** * create Connection耗时100毫秒以上 * * @return */ public static Connection createConnection() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } return ConnectionUtil.getConnectionForPgsql(); } public static Connection getConnection(String key) { try { lock.lock(); if (connectionPool.containsKey(key)) { return connectionPool.get(key); } else { Connection connection = createConnection(); //create Connection connectionPool.put(key, connection); return connection; } } finally { lock.unlock(); } } static ConcurrentMap<String, FutureTask<Connection>> connectionPoolFutureTask = new ConcurrentHashMap<String, FutureTask<Connection>>(); public static Connection getConnectionFutureTask(String key) throws InterruptedException, ExecutionException { FutureTask<Connection> connectionTask = connectionPoolFutureTask.get(key); if (connectionTask != null) { return connectionTask.get(); } else { Callable<Connection> callable = new Callable<Connection>() { @Override public Connection call() { return createConnection(); } }; // create Connection FutureTask<Connection> newTask = new FutureTask<Connection>(callable); connectionTask = connectionPoolFutureTask.putIfAbsent(key, newTask); if (connectionTask == null) { connectionTask = newTask; connectionTask.run(); } return connectionTask.get(); } } static class GetConnectionRun implements Runnable { private int id; public GetConnectionRun(int id) { this.id = id; } @Override public void run() { FutureTaskDemo.getConnection("yangwm" + id); latch.countDown(); } } static class GetConnectionFutureTaskRun implements Runnable { private int id; public GetConnectionFutureTaskRun(int id) { this.id = id; } @Override public void run() { try { FutureTaskDemo.getConnectionFutureTask("yangwm" + id); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException, ExecutionException { int len = 30; long beginTime = 0; // 预热 一下 FutureTaskDemo.getConnectionFutureTask("abc"); FutureTaskDemo.getConnection("abc"); latch = new CountDownLatch(len); beginTime = System.currentTimeMillis(); for (int i = 0; i < len; i++) { new Thread(new GetConnectionFutureTaskRun(i)).start(); } latch.await(); System.out.println("GetConnectionFutureTaskRun Consume time:" + (System.currentTimeMillis() - beginTime) + " ms"); latch = new CountDownLatch(len); beginTime = System.currentTimeMillis(); for (int i = 0; i < len; i++) { new Thread(new GetConnectionRun(i)).start(); } latch.await(); System.out.println("GetConnectionRun Consume time:" + (System.currentTimeMillis() - beginTime) + " ms"); } }

 

 

ThreadPoolExecutorDemo源码

/** * */ package jdk.concurrent; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * 高性能(任务缓冲队列大小为1,直接交给线程执行):SynchronousQueue * 缓冲执行(任务缓冲队列大小为corePoolSize):ArrayBlockingQueue、LinkedBlockingQueue * * new SynchronousQueue<Runnable>()拒绝任务数为400,执行任务数为600,耗时为3438毫秒。 * new ArrayBlockingQueue<Runnable>(10)拒绝任务数为390,执行任务数为610,耗时为6188毫秒。 * new LinkedBlockingQueue<Runnable>(10)拒绝任务数为390,执行任务数为610,耗时为6203毫秒。 * * @author yangwm Aug 12, 2010 4:46:06 PM */ public class ThreadPoolExecutorDemo { final BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>(); //final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10); //final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(10); final ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 600, 30, TimeUnit.SECONDS, queue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); final AtomicInteger completedTask = new AtomicInteger(0); final AtomicInteger rejectedTask = new AtomicInteger(0); static long beginTime; final int count = 1000; public void start() { CountDownLatch latch = new CountDownLatch(count); CyclicBarrier barrier = new CyclicBarrier(count); for (int i = 0; i < count; i++) { new Thread(new TestThread(latch, barrier)).start(); } try { latch.await(); executor.shutdownNow(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("reject task number:" + rejectedTask.get()); System.out.println("execute task number:" + completedTask.get()); System.out.println("end execute cosume time:" + (System.currentTimeMillis() - beginTime) + " ms"); } class TestThread implements Runnable { private CountDownLatch latch; private CyclicBarrier barrier; public TestThread(CountDownLatch latch, CyclicBarrier barrier) { this.latch = latch; this.barrier = barrier; } @Override public void run() { try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } try { executor.execute(new Task(latch)); } catch (RejectedExecutionException exception) { latch.countDown(); rejectedTask.incrementAndGet(); } } } class Task implements Runnable { private CountDownLatch latch; public Task(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } completedTask.incrementAndGet(); //System.out.println("task execute cosume time:" + (System.currentTimeMillis() - beginTime) + " ms"); latch.countDown(); } } /** * @param args */ public static void main(String[] args) { beginTime = System.currentTimeMillis(); ThreadPoolExecutorDemo demo = new ThreadPoolExecutorDemo(); demo.start(); } }

 

ReadWriteLockDemo源码

/** * */ package jdk.concurrent; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 到读锁调用lock方法时,如果没有线程持有写锁,就可获得读锁而无需阻塞等待。 * 到写锁调用lock方法时,如果没有线程持有读锁或写锁,就可获得写锁而无需阻塞等待,否则就需要阻塞等待,因此写操作影响整体性能。 * * 读写锁分离,在读多写少的场景下可大幅度提升性能。 * 使用ReentrantReadWriteLock耗时为328毫秒;改为使用ReentrantLock耗时为10281毫秒; * * @author yangwm Aug 12, 2010 6:08:32 PM */ public class ReadWriteLockDemo { private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private static Lock writeLock = lock.writeLock(); private static Lock readLock = lock.readLock(); private static Map<String, String> maps = new HashMap<String, String>(); private static CountDownLatch latch = new CountDownLatch(102); private static CyclicBarrier barrier = new CyclicBarrier(102); private static ReentrantLock reentrantLock = new ReentrantLock(); static class WirteTask implements Runnable { @Override public void run() { try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } try { writeLock.lock(); maps.put("yangwm", "habby is baskball"); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } finally { writeLock.unlock(); } latch.countDown(); } } static class ReadTask implements Runnable { @Override public void run() { try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } try { readLock.lock(); maps.get("yangwm"); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } finally { readLock.unlock(); } latch.countDown(); } } static class ReentrantWirteTask implements Runnable { @Override public void run() { try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } try { reentrantLock.lock(); maps.put("yangwm", "habby is baskball"); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } finally { reentrantLock.unlock(); } latch.countDown(); } } static class ReentrantReadTask implements Runnable { @Override public void run() { try { barrier.await(); } catch (Exception e) { e.printStackTrace(); } try { reentrantLock.lock(); maps.get("yangwm"); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } finally { reentrantLock.unlock(); } latch.countDown(); } } public static void main(String[] args) throws InterruptedException { long beginTime = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { new Thread(new ReadTask()).start(); } for (int i = 0; i < 2; i++) { new Thread(new WirteTask()).start(); } latch.await(); System.out.println("ReentrantReadWriteLock Consume time:" + (System.currentTimeMillis() - beginTime) + " ms"); latch = new CountDownLatch(102); barrier = new CyclicBarrier(102); beginTime = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { new Thread(new ReentrantReadTask()).start(); } for (int i = 0; i < 2; i++) { new Thread(new ReentrantWirteTask()).start(); } latch.await(); System.out.println("ReentrantLock Consume time:" + (System.currentTimeMillis() - beginTime) + " ms"); } }

 

 

 

 

你可能感兴趣的:(Java并发学习笔记)