Java高并发程序设计(三)——JDK并发包(一)

it2022-05-05  101

引言

读书读了一个多月了,这个月工作很多,空闲时间少,但是我还是在每天上班之前,下班之后挤出零星的时间写写博客,看看社区。每天下班以后,经常搜一些最近学习的相关知识点来加深理解。上次我介绍了前两章的内容,阅读人很少,并不多,但是这并不能打击我的积极性啦~ 今天我们继续讲解第三章的内容。 为了更好的支持并发程序,JDK内部提供了大量实用的API和框架,本章中主要介绍下面三个部分: (1)首先,介绍关于同步控制的工具,之前介绍的synchronized关键字就是一种同步控制的手段,在本章中,将会看到更加丰富多彩的多线程控制方法。 (2)其次,介绍JDK对于线程池的支持,使用线程池,将很大程度上提高线程调度的性能。 (3)最后,向大家介绍JDK的一些并发容器,这些容器专为并行访问设计,是高效,安全,稳定的工具。

本章我将分为三部分,依次给大家进行讲解。

重入锁

使用方法 重入锁完全可以替代sysnchronized关键字。 重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。 package JDK并发包; import java.util.concurrent.locks.ReentrantLock; /** * author:select you from me * func :重入锁的使用方法 */ public class ReentranLockTest implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0; @Override public void run() { for(int j=0;j<10000;j++){ //i++; lock.lock();//必须显示的调用,比synchronized更灵活 try { i++; }finally { lock.unlock();//最后必须释放锁 } } } public static void main(String[] args) throws InterruptedException{ ReentranLockTest tlt = new ReentranLockTest(); Thread t1 = new Thread(tlt); Thread t2 = new Thread(tlt); t1.start(); t2.start(); Thread.sleep(1000); System.out.println(i); } }

这里请注意下: 对同一段代码加两把锁也是可以的! 即:

lock.lock();//必须显示的调用,比synchronized更灵活 lock.lock(); try { i++; }finally { lock.unlock();//最后必须释放锁 lock.unlock();//最后必须释放第二把锁 } 中断响应

对于synchronized来说,要么这个线程获取资源继续执行,要么一直等待,而重入锁提供了锁中断的骚操作。请看下边的代码:

package JDK并发包; import java.util.concurrent.locks.ReentrantLock; /** * author:select you from me * func :重入锁的锁中断 */ public class ReentranLockIntteruptedTest implements Runnable{ public static ReentrantLock reentrantLock1 = new ReentrantLock(); public static ReentrantLock reentrantLock2 = new ReentrantLock(); int lock; /* * 控制加锁顺序,方便构造死锁 */ public ReentranLockIntteruptedTest(int lock){ this.lock=lock; } @Override public void run() { try{ if(lock==1){ reentrantLock1.lockInterruptibly();//这个锁申请动作可以对中断进行响应,我们可以称之为中断锁 try{ Thread.sleep(500); }catch(InterruptedException e){ } reentrantLock2.lockInterruptibly(); } else{ reentrantLock2.lockInterruptibly(); try{ Thread.sleep(500); }catch(InterruptedException e){ } reentrantLock1.lockInterruptibly(); } }catch (InterruptedException e){ e.printStackTrace(); }finally { if(reentrantLock1.isHeldByCurrentThread()){ reentrantLock1.unlock(); } if(reentrantLock2.isHeldByCurrentThread()){ reentrantLock2.unlock(); } System.out.println(Thread.currentThread().getId()+":线程退出"); } } public static void main(String[] args) throws InterruptedException{ ReentranLockIntteruptedTest r1 = new ReentranLockIntteruptedTest(1); ReentranLockIntteruptedTest r2 = new ReentranLockIntteruptedTest(2); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); Thread.sleep(1000); t2.interrupt();//由于t2中断了,线程响应中断,停止执行,线程退出,而t1则正常完成 } }

程序执行结果如下:

10:线程退出 9:线程退出 java.lang.InterruptedException at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) at JDK并发包.ReentranLockIntteruptedTest.run(ReentranLockIntteruptedTest.java:37) at java.lang.Thread.run(Thread.java:745) 锁申请等待限时

避免死锁除了可以采用等待外部通知的手段,还可以采用限时等待的手段。我们来看一下限时等待锁的使用:

package JDK并发包; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** * author:select you from me * func :限时等待锁 */ public class TimeLock implements Runnable{ public static ReentrantLock reentrantLock = new ReentrantLock(); @Override public void run() { try{ if(reentrantLock.tryLock(5, TimeUnit.SECONDS)){//此方法也可以不带参数是用,如果锁被占用,立即返回false Thread.sleep(6000); }else{ System.out.println("get lock failed"); } }catch (InterruptedException e){ e.printStackTrace(); }finally { if(reentrantLock.isHeldByCurrentThread()){ reentrantLock.unlock(); } } } public static void main(String []args){ TimeLock timeLock = new TimeLock(); Thread t1 = new Thread(timeLock); Thread t2 = new Thread(timeLock); t1.start(); t2.start(); } } 公平锁

大多数情况下,系统在锁的等待队列中随机挑选线程,因此不能保证公平性。公平的锁一大特点就是按照时间的先后顺序,保证先到先得,从而不会产生饥饿现象。重入锁为我们提供了公平锁的实现。我们来看下代码:

package JDK并发包; import java.util.concurrent.locks.ReentrantLock; /** * author:select you from me * func :公平锁 */ public class FairLock implements Runnable{ public static ReentrantLock reentrantLock = new ReentrantLock(true);//公平锁的定义 @Override public void run() { while (true){ try{ reentrantLock.lock(); System.out.println(Thread.currentThread().getName()+"获得锁"); }finally { reentrantLock.unlock(); } } } public static void main(String[]args) { FairLock f1 = new FairLock(); Thread t1 = new Thread(f1); Thread t2 = new Thread(f1); t1.start();t2.start(); } }

结果会一直按照这种顺序执行下去

Thread-0获得锁 Thread-1获得锁 Thread-0获得锁 Thread-1获得锁 Thread-0获得锁 Thread-1获得锁 Thread-0获得锁

下一节将要介绍condition,信号量,读写锁等,欢迎继续关注!


作者:select you from me 链接:https://mp.csdn.net/mdeditor/96439280 来源: 转载请联系作者获得授权并注明出处。


最新回复(0)