J.U.C核心有5部分组成,atomic包,locks包,collections包,tools包,executors包
这一篇主要讲locks包,上一篇中的重入锁ReentrantLock类就是实现了locks包中的lock接口。
无论是内部锁synchronized和重入锁ReentrantLock都是控制一次只允许一个线程访问一个资源,信号量可以指定多个线程同时访问资源。提供构造函数,第二个可以指定是否公平,创建信号量对象,必须指定信号量的准入数,每次线程申请得到一个许可,直到等于准入数,则下一个申请的线程会等待,在申请许可的时候也支持响应中断或者尝试获得许可
public Semaphore(int permits)
public Semaphore(int permits,boolean fair)
方法类似于ReentrantLock中的方法
package xidian.lili.testreentrantlock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemapDemo implements Runnable{
//指定最大许可数5,并且是公平锁
public static Semaphore sema=new Semaphore(5,true);
@Override
public void run() {
try {
sema.acquireUninterruptibly();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" done");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
sema.release();
}
}
public static void main(String[] args) {
//妖用线程池创建10个线程
ExecutorService exec =Executors.newFixedThreadPool(10);
SemapDemo fd=new SemapDemo();
for(int i=0;i<10;i++){
exec.submit(fd);
}
}
}
前5个差不多同时输出,一起获得信号量许可,暂停2000秒,在输出。然后释放信号量。然后到后面5个。
ReentrantReadWriteLock这个类跟ReentrantLock没有关系,都是对接口的实现类,ReentrantLock实现的lock,ReentrantReadWriteLock实现了 java.util.concurrent.locks.ReadWriteLock接口,接口中有两个方法Lock readLock()和Lock writeLock();都返回lock对象。
读写锁的原理就是读锁和写锁分离,而读锁是共享锁,写锁是排它锁,使用synchronize和reentrantlock使得读读,读写,写写之间都是同步互斥的关系,但是在读多的情况下,效率低,所以读写锁分离适合读多的情况
package xidian.lili.testreentrantlock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private static Lock lock=new ReentrantLock(); //重入锁
private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
private static Lock readlock=readWriteLock.readLock();//读锁
private static Lock writelock=readWriteLock.writeLock();//写锁
private int value;
private long start;
private long end;
//读锁控制
public Object handleRead(Lock lock) throws InterruptedException{
this.start=System.currentTimeMillis();
try {
lock.lock();
Thread.sleep(1000);//模拟读操作,读操作越耗时,读写锁的优势越明显
return value;
}finally{
lock.unlock();
}
}
//写锁控制
public void handleWrite(Lock lock,int value) throws InterruptedException{
try {
lock.lock();
Thread.sleep(1000);//模拟写操作
this.value=value;
}finally{
this.end=System.currentTimeMillis();
System.out.println("重入锁读18次写一次用时:"+(end-start)+"ms");
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final ReadWriteLockDemo demo=new ReadWriteLockDemo();
Runnable readRunnable=new Runnable(){
@Override
public void run() {
long start=System.currentTimeMillis();
try {
demo.handleRead(lock);//使用重入锁
//demo.handleRead(readlock);使用读锁
} catch (InterruptedException e) {
e.printStackTrace();
}//获得锁
}
};
Runnable writeRunnable=new Runnable(){
@Override
public void run() {
try {
//demo.handleWrite(writelock,new Random().nextInt());//获得写锁
demo.handleWrite(lock,new Random().nextInt());//获得重入锁锁
} catch (InterruptedException e) {
e.printStackTrace();
}//获得读锁
}
};
for(int i=0;i<18;i++){
new Thread(readRunnable).start();
}
for(int i=10;i<11;i++){
new Thread(writeRunnable).start();
}
}
}
结果:
package xidian.lili.testreentrantlock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private static Lock lock=new ReentrantLock(); //重入锁
private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
private static Lock readlock=readWriteLock.readLock();//读锁
private static Lock writelock=readWriteLock.writeLock();//写锁
private int value;
private long start;
private long end;
//读锁控制
public Object handleRead(Lock lock) throws InterruptedException{
this.start=System.currentTimeMillis();
try {
lock.lock();
Thread.sleep(1000);//模拟读操作,读操作越耗时,读写锁的优势越明显
return value;
}finally{
lock.unlock();
}
}
//写锁控制
public void handleWrite(Lock lock,int value) throws InterruptedException{
try {
lock.lock();
Thread.sleep(1000);//模拟写操作
this.value=value;
}finally{
this.end=System.currentTimeMillis();
System.out.println("读写锁分离18次写一次用时:"+(end-start)+"ms");
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final ReadWriteLockDemo demo=new ReadWriteLockDemo();
Runnable readRunnable=new Runnable(){
@Override
public void run() {
long start=System.currentTimeMillis();
try {
//demo.handleRead(lock);//使用重入锁
demo.handleRead(readlock);//使用读锁
} catch (InterruptedException e) {
e.printStackTrace();
}//获得锁
}
};
Runnable writeRunnable=new Runnable(){
@Override
public void run() {
try {
demo.handleWrite(writelock,new Random().nextInt());//获得写锁
//demo.handleWrite(lock,new Random().nextInt());//获得重入锁锁
} catch (InterruptedException e) {
e.printStackTrace();
}//获得读锁
}
};
for(int i=0;i<18;i++){
new Thread(readRunnable).start();
}
for(int i=10;i<11;i++){
new Thread(writeRunnable).start();
}
}
}
结果:
用来控制线程等待,让一个线程等待直到倒计时结束,典型的应用场景是火箭发射,让点火线程等到其他比如安全检查的线程结束在执行
CountDownLatch(int count):count计数器的计数个数
CountDownLatch的await()方法源码分析,无返回值
示例:
package xidian.lili.testreentrantlock;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchDemo implements Runnable {
//表示要有10个线程完成任务
public static CountDownLatch end=new CountDownLatch(10);
static CountDownLatchDemo cdld=new CountDownLatchDemo();
@Override
public void run() {
try {
//模拟检查任务
Thread.sleep(new Random().nextInt(10)*1000);
System.out.println(Thread.currentThread().getName()+"检测安全");
end.countDown();//没有一个线程过来完成任务,计数器减1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec=Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++)
{
exec.submit(cdld);
}
end.await();//主线程等待,等待在end上的线程完成任务,主线程才开始执行下一步
System.out.print("点火");
exec.shutdown();
}
}
完成的功能类似倒计时器,CountDownLatch,也是控制线程之间循环等待,但是他的计数器是可以循环使用的
可以看到返回一个整数,每一一次线程执行await的值,返回值就是增大1,当返回值为parties(int是从0开始返回的)
package xidian.lili.testreentrantlock;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static class Soldier implements Runnable{
private final CyclicBarrier cyclic;
private String name;
public Soldier(CyclicBarrier cyclic, String name) {
super();
this.cyclic = cyclic;
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name+"报道");
System.out.println("计数器的值"+cyclic.await());//等到cyclic的计数器达到设定的值往下执行
doWork();
//System.out.println(name+"任务回来去集合了");
cyclic.await();//等到cyclic的计数器达到设定的值往下执行,循环使用cyclic
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doWork() {
try {
//模拟执行任务
Thread.sleep(Math.abs(new Random().nextInt()%10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"完成任务");
}
}
public static class BarrierRun implements Runnable{
int N;
Boolean flag;
public BarrierRun(int n, Boolean flag) {
super();
N = n;
this.flag = flag;
}
@Override
public void run() {
// 每次cyclic计数完成执行的动作
if(flag){
System.out.println("司令:"+N+"个士兵集合完毕");
flag=false;
}else{
System.out.println("司令:"+N+"个士兵完成任务");
}
}
}
public static void main(String[] args) {
final int N=5;
boolean flag=true;
//5个士兵线程
Thread [] soliders=new Thread[5];
CyclicBarrier cyclic=new CyclicBarrier(5,new BarrierRun(N,flag));
System.out.println("士兵集合");
for(int i=0;i
结果:
是一个非常实用的可以在任何位置阻塞线程的工具,LockSupport.park()用来阻塞线程,unpark()方法唤醒线程。LockSupport阻塞和唤醒线程的功能是依赖于sun.misc.Unsafe
它是用相比于Thread.suspend(),可以避免resume()方法先执行造成死锁,相比于wait方法可以不用获得对象的锁还可以实现限时等待。而且park()方法阻塞线程支持中断响应,它不会抛出InterruptedException异常,而是默默返回
这里的阻塞和唤醒采用类似信号量的机制来实现,每一个线程都有一个许可(只有一个许可),执行LockSupport.park()方法,如果线程的许可还在会立即返回,如果许可不可用,则阻塞,而unpark()方法唤醒线程,也就是将线程的许可置为可用,所以就算unpark()方法先执行了,在调用park方法阻塞的时候,也会消费掉unpark()带来的许可,然后返回。区别于如果resume()方法先执行了,在执行suspend()方法就会造成阻塞的线程永久阻塞。
实例对比:park,unpark程序顺利结束
package xidian.lili.testreentrantlock;
import java.util.concurrent.locks.LockSupport;
public class LockSupportDemo{
public static Object u=new Object();
public static class changeObjectThread extends Thread{
String name;
public changeObjectThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized(u){
System.out.println("in "+Thread.currentThread().getName());
LockSupport.park();
//LockSupport.park(this);//查看因为哪个对象挂起
//Thread.currentThread().suspend();
}
}
}
public static void main(String[] args) throws InterruptedException {
changeObjectThread t1=new changeObjectThread("t1");
changeObjectThread t2=new changeObjectThread("t2");
t1.start();
Thread.sleep(1000);
t2.start();
//t1.resume();
//t2.resume();
LockSupport.unpark(t1);
LockSupport.unpark(t2);
t1.join();
t2.join();//
}
}
使用jstack 查看线程运行情况,我们可以看到t1和t2在过程中因为park被挂起,等待
而且我们也可以产看因为哪个对象挂起,LockSupport.park(this);
用jstack查看线程运行状态:可以看到是在等待哪个对象
但是用suspend和resume:
我们知道由于t2线程先执行了resume方法,所以t2应该是被挂起的,但是这里显示它是Runnable状态