高并发编程三
来源:互联网 发布:豆瓣2017网络剧top10 编辑:程序博客网 时间:2024/04/29 07:22
重入锁
重入锁可以完全替代 synchronized. JDK 5.0,重入锁性能好于 synchronized. JDK 6.0 开始,两者差距不大。重入锁使用 java.util.concurrent.locks.ReenitrantLock 实现。
public class ReentrantLockTest implements Runnable { static ReentrantLock reentrantLock = new ReentrantLock(); static int i = 0; @Override public void run() { for (int j = 0;j < 10000;j++) { //加个重入锁 reentrantLock.lock(); try { i++; } finally { reentrantLock.unlock(); } } } public static void main(String[] args) throws InterruptedException { ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); /*Thread t1 = new Thread(new ReentrantLockTest()); Thread t2 = new Thread(new ReentrantLockTest());*/ Thread t1 = new Thread(reentrantLockTest); Thread t2 = new Thread(reentrantLockTest); t1.start();t2.start(); t1.join();t2.join(); System.out.println(i); } }
重入锁中,不必考虑加锁的对象是否是同一个对象。 与 synchronized 相比,重入锁有显著的操作过程。 开发人员要手动指定何时加锁,何时释放锁。 一个线程连续两次获得同一把锁,这是允许的。加多少次的锁,要释放相同次数的锁。1.1 中断响应对于 synchronized,如果一个线程在等待,那么结果只有两种情况,要么获得这把锁,那么保存等待。使用重入锁,提供另一种可能,就是线程可以被中断。
public class InterruptionLockTest implements Runnable { static ReentrantLock lock1 = new ReentrantLock(); static ReentrantLock lock2 = new ReentrantLock(); int lock; public InterruptionLockTest(int lock) { this.lock = lock; } @Override public void run() { try { if (lock == 1) { System.out.println("线程1 加 lock1 中断锁"); lock1.lockInterruptibly(); System.out.println("线程1 加完 lock1 中断锁"); Thread.sleep(1000); System.out.println("线程1 加 lock2 中断锁"); lock2.lockInterruptibly(); System.out.println("线程1 加完 lock2 中断锁"); } else { System.out.println("线程2 加 lock2 中断锁"); lock2.lockInterruptibly(); System.out.println("线程2 加完 lock2 中断锁"); Thread.sleep(2000); System.out.println("线程2 加 lock1 中断锁"); lock1.lockInterruptibly(); System.out.println("线程2 加完 lock1 中断锁"); } } catch (InterruptedException e) { if (lock1.isHeldByCurrentThread()) { System.out.println("lock1 释放锁"); lock1.unlock(); } if (lock2.isHeldByCurrentThread()) { System.out.println("lock2 释放锁"); lock2.unlock(); } } } public static void main(String[] args) throws InterruptedException { InterruptionLockTest lockTest1 = new InterruptionLockTest(1); InterruptionLockTest lockTest2 = new InterruptionLockTest(2); Thread t1 = new Thread(lockTest1); Thread t2 = new Thread(lockTest2); t1.start();t2.start(); Thread.sleep(5000); System.out.println("线程2 中断"); t2.interrupt(); } }
执行结果:线程1 加 lock1 中断锁线程1 加完 lock1 中断锁线程2 加 lock2 中断锁线程2 加完 lock2 中断锁线程1 加 lock2 中断锁线程2 加 lock1 中断锁线程2 中断lock2 释放锁线程1 加完 lock2 中断锁1.2 锁申请等待限时避免死锁一种方法,等待限时。使用 tryLock() 进行一次限时等待。
public class LimitedTimeLock implements Runnable { static ReentrantLock reentrantLock = new ReentrantLock(); @Override public void run() { try { System.out.println(Thread.currentThread().getId() + " 进入"); if (reentrantLock.tryLock(5, TimeUnit.SECONDS)) { System.out.println(Thread.currentThread().getId() + " 执行"); Thread.sleep(5000); } else { System.out.println(Thread.currentThread().getId() + " 等待超时"); System.out.println("get lock fail"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (reentrantLock.isHeldByCurrentThread()) { reentrantLock.unlock(); } } } public static void main(String[] args) { LimitedTimeLock limitedTimeLock = new LimitedTimeLock(); Thread t1 = new Thread(limitedTimeLock); Thread t2 = new Thread(limitedTimeLock); t1.start();t2.start(); } }
执行结果 11 进入 12 进入 11 执行 12 等待超时 get lock fail1.3公平锁 公平锁会按照时间的先后顺序,保证先到者先得,后到者后得。 特点是:不会产生饥饿现象。 重入锁有一个如下的构造函数: public ReentrantLock(boolean fair) 当参数 fair 为 true 时,表示锁是公平的。公平锁看起来很优美,但是要实现公平锁必然要求系统维护一个有序队列,因此公平锁在实现成本比较高,性能相对也非常低下。 默认情况下,锁是非公平的。
public class FairLock implements Runnable { //声明一个公平锁 static ReentrantLock lock = new ReentrantLock(true); @Override public void run() { while (true) { try { lock.lock(); System.out.println(Thread.currentThread().getName() + " 获得锁"); } finally { lock.unlock(); } } } public static void main(String[] args) { /* FairLock fairLock = new FairLock(); FairLock fairLock1 = new FairLock(); Thread t1 = new Thread(fairLock); Thread t2 = new Thread(fairLock1);*/ FairLock fairLock = new FairLock(); Thread t1 = new Thread(fairLock,"线程_1"); Thread t2 = new Thread(fairLock,"线程_2"); t1.start();t2.start(); } }
执行结果 线程_2 获得锁 线程_1 获得锁 线程_2 获得锁 线程_1 获得锁 线程_2 获得锁 线程_1 获得锁 ReentrantLock的几个重要方法整理: 1) lock() 获得锁,如果锁已经被占用,则等待 2) lockInterruptibly() 获得锁,但优先响应中断 3) tryLock() 尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回 4) tryLock(long time,TimeUnit unit) 在给定时间内尝试获得锁 5) unLock() 释放锁。
condition 条件
condition 可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。condition 接口提供的基本方法:
void await() throws InterruptedException void awaitUninterruptibly() long awaitName(long nanosTimeout) throws InterruptedException boolean await(long time,TimeUnit unit) throws InterruptedException boolean awaitUntil(Date deadline) throws InterruptedException void signal(); void signalAll()
1) await() 使当前线程等待,同时释放当前锁,当其他线程中使用 signal() 或者 signalAll()线程会重新获得锁并继续执行。和 Object.wait() 相似2) awaitUninterruptibly() 和 await() 基本相同,但它并不会在等待过程中响应中断。3) singal() 用于唤醒一个在等待中的线程。和 Object.notify() 类似。
信号量(semaphore)
信号量为多线程协作提供了更强大的控制方法。广义上,信号量是对锁的扩展。
无论是 synchronized 还是 reentrantLock,一次只允许一个线程访问一个资源,
信号量可以指定多个线程,同时访问某一个资源。构造函数:
public Semaphore(int permits)public Semaphore(int permits,boolean fair) //第二个参数可以指定是否公平
构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。
信号量的主要逻辑方法:
public void acquire()public void acquireUninterruptibly()public boolean tryAcquire()public boolean tryAcquire(long timeout,TimeUnit unit)public void release()1) acquire() 尝试获得一个准入的许可,若无法获得,线程会等待,直到有线程释放一个许可或者当前线程被中断。2) acquireUninterruptibly() 和 acquire 类似,但不响应中断。3) tryAcquire() 尝试获得一个许可,如果成功返回true,失败返回false,不会进行等待。4) release() 用于线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。
public class SemapDemo implements Runnable { final Semaphore semaphore = new Semaphore(5); @Override public void run() { try { semaphore.acquire(); Thread.sleep(5000); System.out.println(Thread.currentThread().getId() + " do"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { ExecutorService ex = Executors.newFixedThreadPool(20); final SemapDemo demo = new SemapDemo(); for (int i = 0;i < 20;i++) { ex.submit(demo); } } }
执行结果: 11 do 12 do 13 do 14 do 15 do 16 do 17 do 18 do 19 do 20 do 21 do 22 do 23 do 24 do 25 do 26 do 27 do 28 do 29 do 30 do 五个线程同时执行 申请信号量使用 acquire()操作,在离开时,务必使用 release() 释放信号量。 如果不幸发生了信号量的泄露,那么可以进入临界区的线程数量就会越来越少, 直到所有的线程均不可访问。
ReadWriteLock 读写锁
JDK5 中提供的读写分离锁。读写锁的访问约束情况
读 + 读 = 非阻塞,读 + 写 = 阻塞
写 + 读 = 阻塞, 写 + 写 = 阻塞
import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by wjp on 2016/12/30. */ public class ReadWriteLockDemo { private static Lock lock = new ReentrantLock(); private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private static Lock readLock = readWriteLock.readLock(); private static Lock writteLock = readWriteLock.writeLock(); private int value; public Object handleRead(Lock lock) throws InterruptedException { try { lock.lock(); Thread.sleep(5000); return value; } finally { lock.unlock(); } } public void handleWrite(Lock lock,int index) throws InterruptedException { try { lock.lock(); Thread.sleep(5000); value = index; } finally { lock.unlock(); } } public static void main(String[] args) { final ReadWriteLockDemo demo = new ReadWriteLockDemo(); Runnable readRunnable = new Runnable() { @Override public void run() { try { //demo.handleRead(readLock); demo.handleRead(lock); } catch (InterruptedException e) { e.printStackTrace(); } } }; Runnable writeRunale = new Runnable() { @Override public void run() { try { //demo.handleWrite(writteLock,new Random().nextInt()); demo.handleWrite(lock,new Random().nextInt()); } catch (InterruptedException e) { e.printStackTrace(); } } }; System.out.println("线程开始时间" + System.currentTimeMillis()); for (int i=0;i<18;i++) { new Thread(readRunnable).start(); } for (int i=18;i < 20;i++) { new Thread(writeRunale).start(); } System.out.println("线程结束时间" + System.currentTimeMillis()); } }
使用 读写锁,和使用重入锁,在时间消耗上有明显的区别。
5倒计时器 countDownLatch
countDownLatch 是一个非常实用的多线程控制工具类。
这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再执行。
countDownLatch 的构造函数接收一个整数作为参数public CountDownLatch(int count)
import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by wjp on 2016/12/30. */ public class CountDownLatchDemo implements Runnable { static final CountDownLatch end = new CountDownLatch(10); static final CountDownLatchDemo demo = new CountDownLatchDemo(); @Override public void run() { //模拟检测任务 try { Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("check complete"); end.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { ExecutorService ex = Executors.newFixedThreadPool(10); for (int i =0;i < 10;i++) { ex.submit(demo); } //等待检测 end.await(); System.out.println("fire!"); ex.shutdown(); } }
执行结果:check completecheck completecheck completecheck completecheck completecheck completecheck completecheck completecheck completecheck completefire!
6. 循环栅栏 cyclicBarrier
多线程并发控制使用工具。和 CountDownLatch 非常类似,也可以实现线程间的计数等待,
但是它的功能比 CountDownLatch 更加复杂且强大。
public CyclicBarrier(int parties,Runable barrierAction)barrierAction : 当计数器一次计数完成后,系统会执行的动作。
import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * Created by wjp on 2016/12/30. */ public class CyclicBarrierBemo { public static class Soldier implements Runnable { private String soldier; private final CyclicBarrier cyclicBarrier; Soldier(CyclicBarrier cyclicBarrier,String soldierName) { this.cyclicBarrier = cyclicBarrier; this.soldier = soldierName; } @Override public void run() { //等待所有士兵集合 try { cyclicBarrier.await(); doWork(); //等待所有士兵完成工作 cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } void doWork() { try { Thread.sleep(Math.abs(new Random().nextInt() % 1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(soldier + "任务完成"); } } public static class BarrierRun implements Runnable { boolean flag; int N; public BarrierRun(boolean flag,int N) { this.flag = flag; this.N = N; } @Override public void run() { if (flag) { System.out.println("司令:[士兵" + N + "个,任务完成!]"); } else { System.out.println("司令:[士兵" + N + "个,集合完毕!]"); flag = true; } } } public static void main(String[] args) { final int N = 10; Thread[] allSoldier = new Thread[N]; boolean flag = false; CyclicBarrier cyclicBarrier = new CyclicBarrier(N,new BarrierRun(flag,N)); //设置屏障点,主要是为了执行这个方法 System.out.println("集合队伍!"); for (int i = 0;i < N;i++){ System.out.println("士兵" + i + "报道"); allSoldier[i] = new Thread(new Soldier(cyclicBarrier,"士兵" + i)); allSoldier[i].start(); } } }
集合队伍!士兵0报道士兵1报道士兵2报道士兵3报道士兵4报道士兵5报道士兵6报道士兵7报道士兵8报道士兵9报道司令:[士兵10个,集合完毕!]士兵3任务完成士兵6任务完成士兵5任务完成士兵7任务完成士兵2任务完成士兵1任务完成士兵9任务完成士兵0任务完成士兵4任务完成士兵8任务完成司令:[士兵10个,任务完成!]
线程阻塞工具类 LockSupport
可以在线程内任意位置让线程阻塞。
和 Thread.suspend() 相比,它弥补了由于 resume() 在前发生,导致线程无法继续执行的情况。
和 Object.wait() 相比,它不需要先获得某个对象的锁,也不会抛出 InterruptedExceptionLockSupport 的静态方法 park() 可以阻塞当前线程,类似的有 parkNanos(),parkUntil()
实现了一个限时的等待。
public class LockSupportDemo { public static Object u = new Object(); static ChangeObjectThread t1 = new ChangeObjectThread("t1"); static ChangeObjectThread t2 = new ChangeObjectThread("t2"); public static class ChangeObjectThread extends Thread { public ChangeObjectThread(String name) { super.setName(name); } @Override public void run() { System.out.println("in " + getName()); LockSupport.park(); } } public static void main(String[] args) throws InterruptedException { t1.start(); Thread.sleep(100); t2.start(); LockSupport.unpark(t1); LockSupport.unpark(t2); t1.join(); t2.join(); } }
- 高并发编程三
- Java高并发编程(三)
- 高并发(三)
- nio高并发编程
- java高并发编程
- 高并发编程二
- 高并发编程四
- nio高并发编程
- Java高并发编程
- 高并发网络编程epoll
- Java高并发编程:HandlerThread
- java线程高并发编程
- 高并发编程必备基础
- 高并发编程必备基础
- 高并发编程必备基础
- Erlang并发编程(三) --- 调试
- 并发编程实践三:Condition
- JAVA并发编程笔记三
- Android 2.1.1太慢
- Android Fragment完全解析,关于碎片你所需知道的一切
- Material Design 笔记之 TabLayout
- Python程序设计(二)
- 错误处理(三)—— 剪贴板为空。 (异常来自......)
- 高并发编程三
- usaco Superprime Rib<dfs找回文>
- R资源汇总(待补充)
- java 程序内嵌WEB服务
- Eclipse打不开。The Eclipse executable launcher was unable to locate its companion shared library
- 深入理解Java-Volatile关键字
- Qt设置窗口及窗口相关操作详解
- hadoop体系结构
- sqlserver 中批量删除\r\n 换行符