实战Java高并发程序设计-04 Java并发包锁和其他工具的使用
2017-06-30 16:04
591 查看
并发包(java.util.concurrent)
为了更好的支持并发程序,jdk内部提供了大量实用的API和框架同步控制(锁)
锁(lock)可以完全代替关键字synchronize.jdk中锁是一个接口,提供了三个实现类,读锁,写锁,重入锁.
读写锁
读写锁拆成读锁和写锁来理解。读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候其他线程都已经释放了读锁,而且该线程获取写锁之后,其他线程不能再获取读锁。简单的说就是写锁是排他锁,读锁是共享锁。下面代码开启了10个读取线程,10个写线程
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest { private ReadWriteLock lock = new ReentrantReadWriteLock(); public void read() { try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + " 开始读取"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " 读取完毕"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } public void write() { try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() + " 开始写数据"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " 写数据完毕"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } } public static void main(String[] args) { final ReadWriteLockTest rwlt = new ReadWriteLockTest(); for(int i = 0 ;i < 10 ; i ++){ new Thread() { @Override public void run() { rwlt.read(); } }.start(); new Thread() { @Override public void run() { rwlt.write(); } }.start(); } } }
打印结果
Thread-0 开始读取 Thread-0 读取完毕 Thread-1 开始写数据 Thread-1 写数据完毕 Thread-3 开始写数据 Thread-3 写数据完毕 Thread-2 开始读取 Thread-4 开始读取 Thread-2 读取完毕 Thread-4 读取完毕 Thread-5 开始写数据 Thread-5 写数据完毕 Thread-6 开始读取 Thread-6 读取完毕 Thread-7 开始写数据 Thread-7 写数据完毕 Thread-8 开始读取 Thread-8 读取完毕 Thread-9 开始写数据 Thread-9 写数据完毕 Thread-10 开始读取 Thread-10 读取完毕 Thread-11 开始写数据 Thread-11 写数据完毕 Thread-12 开始读取 Thread-12 读取完毕 Thread-13 开始写数据 Thread-13 写数据完毕 Thread-14 开始读取 Thread-14 读取完毕 Thread-15 开始写数据 Thread-15 写数据完毕 Thread-16 开始读取 Thread-16 读取完毕 Thread-17 开始写数据 Thread-17 写数据完毕 Thread-18 开始读取 Thread-18 读取完毕 Thread-19 开始写数据 Thread-19 写数据完毕
重入锁
可重入锁的概念是自己可以再次获取自己的内部锁。举个例子,比如一条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的(如果不可重入的锁的话,此刻会造成死锁)。说的更高深一点可重入锁是一种递归无阻塞的同步机制。//打印类 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Print { private Lock lock = new ReentrantLock(); public void prt(){ lock.lock(); for(int i = 0 ; i < 5 ; i++){ System.out.println(Thread.currentThread().getName() + " : " + i); } lock.unlock(); } } //线程类 public class Th1 implements Runnable{ private Print p; public Th1(Print p) { this.p = p; } @Override public void run() { p.prt(); } } //Main方法类 public class M { public static void main(String[] args) { Print p = new Print(); new Thread(new Th1(p)).start(); new Thread(new Th1(p)).start(); } }
在重入锁中提供了公平锁和非公平锁 构造的时候传入参数 true/false 来区分
公平锁与非公平锁
公平表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO顺序。而非公平就是一种获取锁的抢占机制,和公平相对就是先来不一定先得,这个方式可能造成某些线程饥饿(一直拿不到锁)Condition(并发包中的wait与notify)
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * Condition 配合Lock 实现线程的等待 与通知 */ public class ConditionTest{ public static ReentrantLock lock=new ReentrantLock(); public static Condition condition =lock.newCondition(); public static void main(String[] args) { new Thread(){ @Override public void run() { lock.lock();//请求锁 try{ System.out.println(Thread.currentThread().getName()+"==》进入等待"); condition.await();//设置当前线程进入等待 }catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock();//释放锁 } System.out.println(Thread.currentThread().getName()+"==》继续执行"); } }.start(); new Thread(){ @Override public void run() { lock.lock();//请求锁 try{ System.out.println(Thread.currentThread().getName()+"=》进入"); Thread.sleep(2000);//休息2秒 condition.signal();//随机唤醒等待队列中的一个线程 System.out.println(Thread.currentThread().getName()+"休息结束"); }catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock();//释放锁 } } }.start(); } }
Semaphore(信号量)
信号量为多线程提供了更为强大的控制方法,无论是锁还是synchronize,一次都只允许一个线程访问一个资源,而信号量可以指定多少个线程,同时访问某一个资源.通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可,
//打印类 import java.util.concurrent.Semaphore; public class Print { private Semaphore semaphore = new Semaphore(5); public void prt(){ try { System.out.println(Thread.currentThread().getName() + " : 准备进入" ); semaphore.acquire(); Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + " : 进入" ); Thread.sleep(3000); semaphore.release(); System.out.println(Thread.currentThread().getName() + " : 离开" ); } catch (Exception e) { e.printStackTrace(); } } } //线程类 public class Th1 implements Runnable{ private Print p; public Th1(Print p) { this.p = p; } @Override public void run() { p.prt(); } } //Main方法类 public class M { public static void main(String[] args) { Print p = new Print(); for(int i = 0 ; i < 14 ; i++){ new Thread(new Th1(p)).start(); } } }
计数器(CountDownLatch)
主线程模拟裁判,八个子线程模拟运动员,裁判和八个运动员都就位以后,运动员才能开始跑步import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownTest { public static void main(String[] args) { ExecutorService pool = Executors.newCachedThreadPool(); final CountDownLatch cdl1 = new CountDownLatch(1);//裁判吹哨开始倒计时,归零运动员开始跑步 final CountDownLatch cdl2 = new CountDownLatch(8);//运动员跑完了,裁判公布结果。//cdl2.countDown() 这个方法 每次到达一个会减少1 而不是一次减完 这里八个运动员跑步 所以这里 实例化对象的 参数 应该是 8 final Map<String , Long> map = new HashMap<String , Long>(); for(int i =0 ; i < 8 ;i ++){//八个运动员 pool.execute(new Runnable() { @Override public void run() { System.out.println("运动员 " + Thread.currentThread().getName() + " 就位"); try { cdl1.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("运动员:" + Thread.currentThread().getName() + "开始跑步"); try { Thread.sleep(new Random().nextInt(10) * 1000); } catch (InterruptedException e) { e.printStackTrace(); } map.put(Thread.currentThread().getName(), new Date().getTime()); System.out.println("运动员:" + Thread.currentThread().getName() + " 跑完"); cdl2.countDown(); } }); } long l = 0; try { System.out.println("裁判就位"); Thread.sleep(3000); cdl1.countDown(); System.out.println("裁判发了起跑消息,运动员起跑 , 裁判等待跑完"); l = new Date().getTime(); cdl2.await(); System.out.println("跑完了,裁判发布结果"); System.out.println("结果如下"); } catch (InterruptedException e) { e.printStackTrace(); } pool.shutdown(); for(Entry<String, Long> entry : map.entrySet()){ System.out.println(entry.getKey() + " 所用所用时间是 :" + (entry.getValue() - l)); } } }
运动员 pool-1-thread-1 就位 运动员 pool-1-thread-4 就位 运动员 pool-1-thread-3 就位 运动员 pool-1-thread-2 就位 裁判就位 运动员 pool-1-thread-6 就位 运动员 pool-1-thread-7 就位 运动员 pool-1-thread-5 就位 运动员 pool-1-thread-8 就位 裁判发了起跑消息,运动员起跑 , 裁判等待跑完 运动员:pool-1-thread-1开始跑步 运动员:pool-1-thread-3开始跑步 运动员:pool-1-thread-2开始跑步 运动员:pool-1-thread-4开始跑步 运动员:pool-1-thread-8开始跑步 运动员:pool-1-thread-5开始跑步 运动员:pool-1-thread-7开始跑步 运动员:pool-1-thread-6开始跑步 运动员:pool-1-thread-3 跑完 运动员:pool-1-thread-5 跑完 运动员:pool-1-thread-7 跑完 运动员:pool-1-thread-6 跑完 运动员:pool-1-thread-2 跑完 运动员:pool-1-thread-8 跑完 运动员:pool-1-thread-1 跑完 运动员:pool-1-thread-4 跑完 跑完了,裁判发布结果 结果如下 pool-1-thread-4 所用所用时间是 :7999 pool-1-thread-5 所用所用时间是 :2999 pool-1-thread-2 所用所用时间是 :5998 pool-1-thread-3 所用所用时间是 :998 pool-1-thread-1 所用所用时间是 :7998 pool-1-thread-7 所用所用时间是 :2999 pool-1-thread-6 所用所用时间是 :3999 pool-1-thread-8 所用所用时间是 :5999
CyclicBarrier(循环栅栏)
循环栅栏与计算器很像,但是可以反复使用,下面模拟 十个人一起去景点的场景.//线程类 package c; import java.util.Random; import java.util.concurrent.CyclicBarrier; public class Th1 implements Runnable{ private CyclicBarrier cb; public Th1(CyclicBarrier cb2) { this.cb = cb2; } @Override public void run() { try { Thread.sleep(new Random().nextInt(10) * 1000); System.out.println(Thread.currentThread().getName() + "到了黄鹤楼 已经有:" + (cb.getNumberWaiting() + 1) + "个到达黄鹤楼"); if(cb.getNumberWaiting() == 10){ System.out.println("全部到齐,下一站龟山"); } cb.await(); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println(Thread.currentThread().getName() + "到了龟山 已经有:" + (cb.getNumberWaiting() + 1) + "个到达龟山"); if(cb.getNumberWaiting() == 10){ System.out.println("全部到齐,下一站东湖"); } cb.await(); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println(Thread.currentThread().getName() + "到了东湖 已经有:" + (cb.getNumberWaiting() + 1) + "个到达东湖"); if(cb.getNumberWaiting() == 10){ System.out.println("全部到齐,结束"); } cb.await(); } catch (Exception e1) { e1.printStackTrace(); } } } //Main方法类 package c; import java.util.concurrent.CyclicBarrier; public class M { public static void main(String[] args) { CyclicBarrier cb = new CyclicBarrier(10); for(int i = 0 ; i < 10 ; i++){ new Thread(new Th1(cb)).start(); } } }
打印结果
Thread-3到了黄鹤楼 已经有:1个到达黄鹤楼 Thread-7到了黄鹤楼 已经有:2个到达黄鹤楼 Thread-8到了黄鹤楼 已经有:3个到达黄鹤楼 Thread-9到了黄鹤楼 已经有:4个到达黄鹤楼 Thread-1到了黄鹤楼 已经有:5个到达黄鹤楼 Thread-5到了黄鹤楼 已经有:6个到达黄鹤楼 Thread-2到了黄鹤楼 已经有:7个到达黄鹤楼 Thread-0到了黄鹤楼 已经有:7个到达黄鹤楼 Thread-6到了黄鹤楼 已经有:9个到达黄鹤楼 Thread-4到了黄鹤楼 已经有:10个到达黄鹤楼 Thread-6到了龟山 已经有:1个到达龟山 Thread-1到了龟山 已经有:1个到达龟山 Thread-5到了龟山 已经有:3个到达龟山 Thread-3到了龟山 已经有:4个到达龟山 Thread-8到了龟山 已经有:4个到达龟山 Thread-2到了龟山 已经有:6个到达龟山 Thread-4到了龟山 已经有:7个到达龟山 Thread-9到了龟山 已经有:8个到达龟山 Thread-7到了龟山 已经有:9个到达龟山 Thread-0到了龟山 已经有:10个到达龟山 Thread-3到了东湖 已经有:1个到达东湖 Thread-0到了东湖 已经有:2个到达东湖 Thread-8到了东湖 已经有:3个到达东湖 Thread-4到了东湖 已经有:4个到达东湖 Thread-2到了东湖 已经有:4个到达东湖 Thread-5到了东湖 已经有:4个到达东湖 Thread-6到了东湖 已经有:7个到达东湖 Thread-1到了东湖 已经有:7个到达东湖 Thread-9到了东湖 已经有:9个到达东湖 Thread-7到了东湖 已经有:10个到达东湖
相关文章推荐
- 使用JRockit作为工具检测并解决JAVA内存泄漏问题的一次实战.doc
- java内存溢出分析工具:jmap使用实战
- JAVA高并发程序设计学习:Synchronized同步代码块具体使用方法
- JDK同步控制工具,JAVA高并发程序设计
- 实战Java高并发程序设计之ReentrantLock(一)
- 【转】使用JRockit作为工具检测并解决JAVA内存泄漏问题的一次实战
- 【java并发】线程同步工具Exchanger的使用
- 实战Java高并发程序设计之概念
- 【实战Java高并发程序设计】5:让普通变量也享受原子操作
- Java并发编程实战 - 第8章 线程池的使用
- java并发之工具类的使用(三)
- 【java并发】线程同步工具Semaphore的使用
- Java并发和多线程4:使用通用同步工具CountDownLatch实现线程等待
- 实战Java高并发程序设计之多线程基础
- 解决Java读取properties文件的中文问题的新办法(不使用native2ascii.exe及其他工具)
- 使用java并发工具栅栏(CyclicBarrier)实现多线程等待,同一时刻执行共同任务
- 【实战Java高并发程序设计 5】让普通变量也享受原子操作
- 04.Java多线程并发库API使用3
- 实战Java高并发程序设计之Condition
- 实战Java高并发程序设计之Java内存模型和线程安全