您的位置:首页 > 编程语言 > Java开发

实战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个到达东湖
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 并发