2019独角兽企业重金招聘Python工程师标准>>>
最近在网上看到一个将concurrent包的系列文章,仔细读了一遍,感觉不错。
分享一下:http://www.hetaoblog.com/myblogs/post/%E8%AF%B4%E4%B8%80%E8%AF%B4java%E7%9A%84concurrent%E5%8C%851-%E6%95%B4%E4%BD%93%E4%BB%8B%E7%BB%8D.jhtml
Concurrent包主要有三个package组成。java.util.concurrent:提供大部分关于并发的接口和类,如BlockingQueue,Callable,ConcurrentHashMap,ExecutorService, Semaphore等。
java.util.concurrent.atomic:提供所有原子操作的类, 如AtomicInteger, AtomicLong等;
java.util.concurrent.locks:提供锁相关的类, 如Lock, ReentrantLock, ReadWriteLock, Condition等;
CountDownLatch
CountDownLatch等待多个线程完成执行,这个类会等待多个子线程结束后才开始执行主线程的其他操作。直接上一个例子。
@Testpublic void testCountDown(){int count = 10;final CountDownLatch l = new CountDownLatch(count);for (int i = 0; i < count; ++i){final int index = i;new Thread(new Runnable() {@Overridepublic void run() {try {Thread.currentThread().sleep(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("thread " + index + " has finished...");l.countDown();}}).start();}try {l.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("now all threads have finished");}
这个例子中,结束语句会在所有线程结束后再执行,否则会一直await。
Atomic类
Atomic相关类是线程安全的,支持无阻塞无锁定的操作set(),get(),getAndSet(), getAndIncrement(),getAndDecrement(),getAndAdd()
@Testpublic void testAtomic(){final int loopcount = 10000;int threadcount = 10;final NonSafeSeq seq1 = new NonSafeSeq();final SafeSeq seq2 = new SafeSeq();final CountDownLatch l = new CountDownLatch(threadcount);for(int i = 0; i < threadcount; ++i){final int index = i;new Thread(new Runnable() {@Overridepublic void run() {for(int j = 0; j < loopcount; ++j){seq1.inc();seq2.inc();}System.out.println("finished : " + index);l.countDown();}}).start();}try {l.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("both have finished....");System.out.println("NonSafeSeq:" + seq1.get());System.out.println("SafeSeq with atomic: " + seq2.get());}
class NonSafeSeq{private long count = 0;public void inc(){count++;}public long get(){return count;}
}class SafeSeq{private AtomicLong count = new AtomicLong(0);public void inc(){count.incrementAndGet();}public long get(){return count.longValue();}
}
输出结果如下:
both have finished....
NonSafeSeq:93212
SafeSeq with atomic: 100000
很明显看出来在原始方法中即使做了10000次相加操作也没有办法保证多线程下的一致性。使用AtomicLong进行相加可以避免这样的问题。
ReentrantLock类
之前一直是使用synchronized关键字来保证线程间的同步。concurrent包提供了ReentrantLock类,该类提供lock()和unlock()方法
1. 是更好的性能,
2. 提供同一个lock对象上不同condition的信号通知
3. 还提供lockInterruptibly这样支持响应中断的加锁过程,意思是说你试图去加锁,但是当前锁被其他线程hold住,然后你这个线程可以被中断;
这里直接上下相关lock操作的代码
class SafeSeqWithLock{private long count = 0;private ReentrantLock lock = new ReentrantLock();public void inc(){lock.lock();try{count++;}finally{lock.unlock();}}public long get() {return count;}}
可以看到,这里在++操作之前先lock了,然后再执行了unlock操作。
ReadWriteLock
读锁可以有很多个锁同时上锁,只要当前没有写锁;
写锁是排他的,上了写锁,其他线程既不能上读锁,也不能上写锁;同样,需要上写锁的前提是既没有读锁,也没有写锁;
试图获得写锁的线程只有当另外一个线程将读锁释放了以后才可以获得
@Testpublic void testRWLock_getw_onr(){ReentrantReadWriteLock lock = new ReentrantReadWriteLock();final Lock rlock = lock.readLock();final Lock wlock = lock.writeLock();final CountDownLatch l = new CountDownLatch(2);// start r threadnew Thread(new Runnable() {@Overridepublic void run() {System.out.println(new Date() + "now to get rlock");rlock.lock();try {Thread.currentThread().sleep(3 * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(new Date() + "now to unlock rlock");rlock.unlock();l.countDown();}}).start();// start w threadnew Thread(new Runnable() {@Overridepublic void run() {System.out.println(new Date() + "now to get wlock");wlock.lock();System.out.println(new Date() + "now to unlock wlock");wlock.unlock();l.countDown();}}).start();try {l.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(new Date() + "finished");}
总结一下。这里是concurrent包中常用的几个类。集中涉及了多线程,并发控制,锁的相关机制。