JDK并发包一

同步控制是并发程序必不可少的重要手段,synchronized是一种最简单的控制方法。但JDK提供了更加强大的方法————重入锁

重入锁

重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。

ReentrantLock有几个重要的方法

  • lock()获得锁,如果锁已占用,则等待。
  • lockInterruptibly()获得锁,优先响应中断。
  • tryLock() 尝试获得锁若成功返回true 失败返回false 不等待立即返回
  • tryLock(long time,TimeUnit unit) 在给定的时间内进行等待
  • unlock()释放锁
  1. public class MyTest {
  2. public static ReentrantLock lock = new ReentrantLock();
  3. public static int i = 0;
  4. public static void main(String[] args) throws InterruptedException {
  5. Thread thread1 = new Thread(new ReenterLock());
  6. Thread thread2 = new Thread(new ReenterLock());
  7. thread1.start();
  8. thread2.start();
  9. thread1.join();
  10. thread2.join();
  11. System.out.println(i);
  12. }
  13. public static class ReenterLock implements Runnable {
  14. @Override
  15. public void run() {
  16. for (int j = 0; j < 1000000; j++) {
  17. lock.lock();
  18. try {
  19. i++;
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24. }
  25. }
  26. }

与synchronized相比重入锁更加灵活 一个线程可以多次获取锁,获取多少次一定要释放多少次

中断响应

对于synchronized相比如果一个线程等待锁,只有两种情况,要么获得锁继续执行,要么等待。重入锁可以有另一种情况————中断thread2.interrupt()

  1. /**
  2. * 中断响应
  3. */
  4. public static class IntLock implements Runnable {
  5. int lock;
  6. public static ReentrantLock lock1=new ReentrantLock();
  7. public static ReentrantLock lock2=new ReentrantLock();
  8. public IntLock(int lock) {
  9. this.lock = lock;
  10. }
  11. @Override
  12. public void run() {
  13. try {
  14. if (lock==1){
  15. lock1.lockInterruptibly();
  16. Thread.sleep(500);
  17. lock2.lockInterruptibly();
  18. }else {
  19. lock2.lockInterruptibly();
  20. Thread.sleep(500);
  21. lock1.lockInterruptibly();
  22. }
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }finally {
  26. if (lock1.isHeldByCurrentThread())
  27. lock1.unlock();
  28. if (lock2.isHeldByCurrentThread())
  29. lock2.unlock();
  30. System.out.println(Thread.currentThread().getName()+":线程退出");
  31. }
  32. }
  33. }
  34. public static void main(String[] args) throws InterruptedException {
  35. Thread thread1 = new Thread(new IntLock(1));
  36. Thread thread2 = new Thread(new IntLock(2));
  37. thread1.start();
  38. thread2.start();
  39. Thread.sleep(2000);
  40. thread2.interrupt();
  41. }

thread2获得了thread1的锁thread1获得了thread2的锁两个线程相互等待。 程序形成死锁thread2.interrupt()执行后thread2中断 自动释放获得的所有锁,thread1可以继续执行。真正完成的只有thread1

锁申请等待限时

除了上面中断通过外部通知的方式避免死锁还有另一种方法,限时等待tryLock()

tryLock()方法有两个参数一个表示等待时长,一个是计时单位,在设置的时间内线程会请求获取锁,超过设置的时间还没获得锁就会返回false。获得成功就返回true

公平锁

在大多数情况下锁都是非公平的,即线程不是按照请求在锁不可用时锁的先后顺序获得锁的,系统只会从等待的线程中随机选一个,而公平锁会按照请求顺序在锁可用是分配锁。ReentrantLock lock1=new ReentrantLock(true);创建锁时在构造方法传入true时此锁就是公平锁,公平锁需要一个有序队列来实现因此成本较高性能低下没特殊要求一般不用。

Condition条件

使用synchronized进行通知就使用Object.wait() ,Object.notify() ,使用锁时Condition的方法几乎相同。

  • await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或者signalAll()方法时线程会重现获得锁继续执行,当线程中断时也能跳出等待
  • awaitUninterruptibly()方法与await()基本相同,但是它并不会在等待过程中响应中断
  • singnal()方法用于唤醒一个等待中的线程,signalAll()唤醒所有线程
  1. public static ReentrantLock lock = new ReentrantLock();
  2. public static Condition condition = lock.newCondition();
  3. public static void main(String[] args) {
  4. Thread thread = new Thread(new ReenterLockCondition());
  5. thread.start();
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. lock.lock();
  12. condition.signal();
  13. lock.unlock();
  14. }
  15. public static class ReenterLockCondition implements Runnable {
  16. @Override
  17. public void run() {
  18. try {
  19. lock.lock();
  20. condition.await();
  21. System.out.println("线程开始执行");
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. } finally {
  25. if (lock.isHeldByCurrentThread()) {
  26. lock.unlock();
  27. }
  28. }
  29. }
  30. }

在执行Conidition.await()之前要求当前线程必须持有相关的重入锁,执行signal()也一样也需要先持有重入锁,在singnal()执行后一般需要释放相关重入锁,否则唤醒的线程无法重新获得相关重入锁无法继续执行。

读写锁

读写锁是分离读操作与写(修改,删除,添加)的锁,可以更大程度提高性能。

非阻塞 阻塞
阻塞 阻塞

如果一个系统中有大量的读操作,那么读写锁就能发挥更大功效。

  1. public static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();//读写锁
  2. public static Lock read = readWriteLock.readLock();//读锁
  3. public static Lock write=readWriteLock.writeLock();//写锁

倒计时器

对于某个线程需要等待其他几个线程执行完毕自己再执行比较适用

假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

  1. public static final CountDownLatch end = new CountDownLatch(10);
  2. public static void main(String[] args) {
  3. ExecutorService executorService = Executors.newFixedThreadPool(10);
  4. for (int i = 0; i < 10; i++) {
  5. executorService.execute(new CountDownLatchDemo());
  6. }
  7. try {
  8. end.await();
  9. System.out.println("准备完毕");
  10. executorService.shutdown();
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. public static class CountDownLatchDemo implements Runnable {
  16. @Override
  17. public void run() {
  18. try {
  19. Thread.sleep(new Random().nextInt(10)*1000);
  20. System.out.println(Thread.currentThread().getName()+"准备完毕");
  21. end.countDown();
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

循环栅栏

CyclicBarrier实现的功能与CountDownLatch类似但刚强大,CyclicBarrier可以实现计数器的复用,

  1. public class Soldier implements Runnable{
  2. private String soldier;
  3. private CyclicBarrier cyclicBarrier;
  4. public Soldier(String soldier, CyclicBarrier cyclicBarrier) {
  5. this.soldier = soldier;
  6. this.cyclicBarrier = cyclicBarrier;
  7. }
  8. public void doWork(){
  9. try {
  10. Thread.sleep(Math.abs(new Random().nextInt()%10000));
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println(this.soldier+"任务完成");
  15. }
  16. @Override
  17. public void run() {
  18. try {
  19. this.cyclicBarrier.await();
  20. doWork();
  21. this.cyclicBarrier.await();
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. } catch (BrokenBarrierException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }
  1. public class BarrierRun implements Runnable {
  2. private boolean flag;
  3. private int i;
  4. public BarrierRun(boolean flag, int i) {
  5. this.flag = flag;
  6. this.i = i;
  7. }
  8. @Override
  9. public void run() {
  10. if (this.flag) {
  11. System.out.println("任务完成");
  12. }else {
  13. System.out.println("集合完毕");
  14. }
  15. }
  16. }
  1. public static void main(String[] args) {
  2. Thread[] threads=new Thread[10];
  3. CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new BarrierRun(false, 10));
  4. System.out.println("队伍集合");
  5. for (int i = 0; i < 10; i++) {
  6. System.out.println("士兵"+i+"报道");
  7. threads[i]=new Thread(new Soldier("士兵"+i,cyclicBarrier));
  8. threads[i].start();
  9. }
  10. }

JDK并发包一的更多相关文章

  1. Java 并发编程实践基础 读书笔记: 第三章 使用 JDK 并发包构建程序

    一,JDK并发包实际上就是指java.util.concurrent包里面的那些类和接口等 主要分为以下几类: 1,原子量:2,并发集合:3,同步器:4,可重入锁:5,线程池 二,原子量 原子变量主要 ...

  2. Java并发程序设计(四)JDK并发包之同步控制

    JDK并发包之同步控制 一.重入锁 重入锁使用java.util.concurrent.locks.ReentrantLock来实现.示例代码如下: public class TryReentrant ...

  3. Java多线程--JDK并发包(2)

    Java多线程--JDK并发包(2) 线程池 在使用线程池后,创建线程变成了从线程池里获得空闲线程,关闭线程变成了将线程归坏给线程池. JDK有一套Executor框架,大概包括Executor.Ex ...

  4. Java多线程--JDK并发包(1)

    Java多线程--JDK并发包(1) 之前介绍了synchronized关键字,它决定了额一个线程是否可以进入临界区:还有Object类的wait()和notify()方法,起到线程等待和唤醒作用.s ...

  5. 3 JDK并发包

    JDK内部提供了大量实用的API和框架.本章主要介绍这些JDK内部功能,主要分为3大部分: 首先,介绍有关同步控制的工具,之前介绍的synchronized就是一种同步控制手段,将介绍更加丰富的多线程 ...

  6. 第3章 JDK并发包(三)

    3.2 线程复用:线程池 一种最为简单的线程创建和回收的方法类似如下代码: new Thread(new Runnable() { @Override public void run() { // d ...

  7. JDK并发包二

    JDK并发包二 线程复用--线程池 在线程池中,总有那么几个活跃的线程,当程序需要线程时可以从池子中随便拿一个控线程,当程序执行完毕,线程不关闭,而是将这个线程退会到池子,等待使用. JDK提供了一套 ...

  8. 第3章 JDK并发包(五)

    3.3 不要重复发明轮子:JDK的并发容器 3.3.1 超好用的工具类:并发集合简介 JDK提供的这些容器大部分在java.util.concurrent包中. ConcurrentHashMap:这 ...

  9. java多线程:jdk并发包的总结(转载)

    转载地址:http://blog.csdn.net/yangbutao/article/details/8479520 1.java 高并发包所采用的几个机制(CAS,volatile,抽象队列同步) ...

随机推荐

  1. Django(26)HttpResponse对象和JsonResponse对象

    HttpResponse对象 Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个HttpRequest对象传给视图函数.那么视图函数在处理完相关的逻辑后,也需要返回一个响 ...

  2. Spring Boot 2.5.0 发布:支持Java16、Gradle 7、Datasource初始化机制调整

    今年520的事情是真的多,娱乐圈的我们不管,就跟DD一起来看看 Spring Boot 2.5.0 的发布吧!看看都带来了哪些振奋人心的新特性和改动! 主要更新 支持 Java 16 支持 Gradl ...

  3. JAVA基础——运算符号

    运算符(java) 算数运算符:+,-,*,/,%(取余),++,-- 赋值运算符:= 关系运算符:<, >, >= ,<= ,== , != 逻辑运算符:&& ...

  4. Otter远程调试

    Otter远程调试 环境配置: 机器 172.16.0.2 172.16.0.3 172.16.0.4 ZK FOLLOWER FOLLOWER LEADER MySQL Manager MySQL ...

  5. IDEA workspace.xml 在 git 中无法忽略 ignore 问题

    问题描述 关于 .idea 的文件夹中的 workspace.xml 设置 ignore 之后每次 commit 依旧提示需要提交改变,这就会导致, 每次merge就会导致提示"本地文件改变 ...

  6. MyBatis 项目开发中是基于 XML 还是注解?

    只要你对 MyBatis 有所认识和了解,想必知道 MyBatis 有两种 SQL 语句映射模式,一种是基于注解,一种是基于XML. 基于 XML <mapper namespace=" ...

  7. 【错误解决】The prefix "context" for element "context:component-scan" is not bound

    在配置spring相关的applicationContext.xml文件时报以上错误 原因是缺失context的namespace. http://www.springframework.org/sc ...

  8. springboot项目部署docker服务器提供api

    1.先将springboot项目打包,我这里用的是IDEA工具打包,打包完成后的jar包在 项目目录/target 中 2.打包完成后进入服务器器终端,将jar包上传到自己设置的目录中,这个目录需要跟 ...

  9. 如何使用 IoC

    创建Maven工程,pom.xml添加依赖 <?xml version="1.0" encoding="UTF-8"?> <project x ...

  10. Vue之前后端交互

    Vue之前后端交互 一.前后端交互模式 接口调用方式 原生ajax 基于jQuery的ajax fetch axios 异步 JavaScript的执行环境是「单线程」 所谓单线程,是指JS引擎中负责 ...