java 多线程工具类_Java 多线程工具类

notify wait

wait()、notify()和notifyAll()是Object类中的native final方法(除重载),这两个方法是为了解决多线程环境下的竞态环境,必须在同步方法块内调用

notify,notifyAll 唤起其他等待锁的线程,但并不会立刻切换到其他线程,而是等到执行完同步代码块之后再进行切换,使用notify会唤起最先等待的线程,notifyAll会唤起所有等待的线程

wait 使当前线程阻塞,释放所持有的锁(不必等到同步代码块结束)

不建议直接使用notify wait进行多线程编程

模拟使用notify的场景:两个线程竞争同一资源

//two threads competing for one resource

public class CompetingThread {

Thread[] threads;

CompetingThread(Thread[] threads){

this.threads= threads;

}

public void get() {

Thread t1 = threads[0], t2 = threads[1];

System.out.println("Ready to Get");

synchronized (this){

System.out.println(Thread.currentThread().getName()+" Got the lock");

System.out.println("Now, "+t1.getName()+" is "+t1.getState()+", "+t2.getName()+" is "+t2.getState());

System.out.println("notify() to release the lock after all done");

System.out.println("Do sth. before notify is just the same as after");

notify();

System.out.println("Do sth. after notify is just the same as before");

System.out.println(Thread.currentThread().getName()+" is done.");

}

}

public static void main(String[] args) {

Thread[] threads = new Thread[2];

CompetingThread competingThread = new CompetingThread(threads);

threads[0] = new Thread(()->competingThread.get());

threads[1] = new Thread(()->competingThread.get());

threads[0].start();

threads[1].start();

}

}

输出

//output

Ready to Get

Ready to Get

Thread-0 Got the lock

Now, Thread-0 is RUNNABLE, Thread-1 is BLOCKED

notify() to release the lock after all done

Do sth. before notify is just the same as after

Do sth. after notify is just the same as before

Thread-0 is done.

Thread-1 Got the lock

Now, Thread-0 is TERMINATED, Thread-1 is RUNNABLE

notify() to release the lock after all done

Do sth. before notify is just the same as after

Do sth. after notify is just the same as before

Thread-1 is done.

模拟使用wait和notify(notifyAll)的场景:顾客在餐厅点餐,服务员等候(BLOCKING),点餐完毕,顾客等候上菜(wait()),服务员上菜,告知顾客菜来了(notify())

public class WaitNotifyExample {

public static Object menu = new Object();

public static void main(String[] args) {

Thread customer = new Thread() {

@Override

public void run() {

synchronized (menu) {

System.out.println("Customer: I'm reading menu, please wait");

try {

Thread.sleep(1000);

System.out.println("Customer: These are my orders.(waiting for the meal)");

menu.wait();

}catch (InterruptedException e){}

System.out.println("Customer: Oh, these are my foods!");

}

}

};

Thread waiter = new Thread(){

@Override

public void run(){

System.out.println("Waiter: (Waiting for customer's orders)");

synchronized (menu){

System.out.println("Waiter: Gonna prepare you meal, sir.");

try {

Thread.sleep(1000);

menu.notify();//notifyAll if there are more than 1 customer

System.out.println("Waiter: Your foods are ready.");

}catch (InterruptedException e){}

}

}

};

customer.start();

waiter.start();

}

}

输出

//output

Customer: I'm reading menu, please wait

Waiter: (Waiting for customer's orders)

Customer: These are my orders.(waiting for the meal)

Waiter: Gonna prepare you meal, sir.

Waiter: Your foods are ready.

Customer: Oh, these are my foods!

CountDownLatch

一个线程安全的倒计时器,在倒计时为0前,令线程等待(getState():WAITING)

使用步骤:

构造一个容量为10的倒计时器 CountDownLatch latch = new CountDownLatch(10)

主线程中调用latch.await(),等待相关线程进行倒计时

不同的线程中调用该倒计时器实例的latch.countDown()方法,进行倒计时

当latch的倒计时为0时,主线程恢复运行

ReentrantLock

ReentrantLock(Re-entrantLock)是一个基于AQS(AbstractQueuedSynchronizer)高性能工具,支持线程在未释放锁的情况下重复获取锁

API

方法

解释

lock

holdCount+1,并给当前线程加锁

unlock

holdCount-1,holdCount为0时,释放资源

tryLock

如果资源锁不在其他线程中,返回true,holdCount+1,否则返回false,不过线程并不会阻塞

lockInterruptibly()

锁空闲的情况下正常获取,但是允许被其他线程的请求中断

getHoldCount

获取holdCount,注意如果持有锁的不是本线程,则直接返回0

原理概述

ReentrantLock的可重入功能基于AQS的同步状态:state(可通过getHoldCount获取)。当某一线程获取锁后,holdCount+1,并记录下当前持有锁的线程,再有线程来获取锁时,判断这个线程与持有锁的线程是否是同一个线程,如果是,holdCount+1,如果不是,阻塞线程。

当线程释放锁时,holdCount-1,holdCount为0时,唤醒其他线程,使其重新竞争锁。

解决问题

解决synchronized在竞争激烈场景下的性能问题

在锁竞争不激烈的时候,多数情况下锁会停留在偏向锁和轻量级锁阶段,这两个阶段下,synchronized性能很好,但当存在大量线程竞争锁时,可能会膨胀为重量级锁,性能下降,ReentrantLock的性能会优于synchronized。不过在JDK1.6之后,synchronized进行了优化,大多数场景下性能与ReentrantLock所差无几

填补其他synchronized的缺陷

一旦线程卡在等待锁的阶段,就有可能出现死锁

不可响应中断

不能尝试获取锁

ReentrantLock VS synchronized

对多线程操作支持情况和实现方式(如果支持的话)

项目\锁

synchronized

ReentrantLock

公平性

只支持非公平锁

支持公平锁和非公平锁

是否支持尝试获取锁

不支持

支持 tryLock(time, TimeUnit)

是否可响应中断

不支持

支持 lockInterruptibly

等待条件

支持 notify wait

支持 Condition

ReentrantLock Example

tryLock,lock,lockInterruptibly的对比测试,runnableWork是一个耗时操作,目的是让线程状态保持在RUNNABLE

public class ReentrantLockAPI {

public static void runnableWork(){

for (int i = 0; i < 1000; i++) {

for (int j = 0; j < 1000; j++) {

for (int k = 0; k < 1000; k++) {

}

}

}

}

public static void lockInterruptibly_tryLock_API(){

final ReentrantLock lock = new ReentrantLock();

Thread interruptibleThread = new Thread(()->{

try {

runnableWork();

lock.lockInterruptibly();

System.out.println("ReentrantLock is locked by "+Thread.currentThread().getName());

}catch (InterruptedException e){

System.out.println(Thread.currentThread().getName()+" was interrupted while getting lock");

}finally {

if(lock.isHeldByCurrentThread()) {

System.out.println("ReentrantLock is released by "+Thread.currentThread().getName());

lock.unlock();

}

}

});

Thread uninterruptibleThread = new Thread(()->{

System.out.println("ReentrantLock is locked by "+Thread.currentThread().getName());

try {

runnableWork();

lock.lock();

}catch (Exception e){

System.out.println(Thread.currentThread().getName()+" was interrupted.");

}finally {

System.out.println("ReentrantLock is released by "+Thread.currentThread().getName());

lock.unlock();

}

});

Thread interruptThread = new Thread(()->{

//uninterruptibleThread.interrupt();

interruptibleThread.interrupt();

});

interruptibleThread.start();

// uninterruptibleThread.start();

interruptThread.start();

}

public static void lock_tryLock_API(){

ReentrantLock lock = new ReentrantLock();

Thread t1 = new Thread(()->{

System.out.println("ReentrantLock is locked by "+Thread.currentThread().getName());

lock.lock();

try {

Thread.sleep(1000);

}catch (Exception e){}

System.out.println("ReentrantLock is released by "+Thread.currentThread().getName());

lock.unlock();

});

Thread t2 = new Thread(()->{

try {

System.out.println("Try to get the lock, if not, wait for sometime");

boolean res = lock.tryLock(500l, TimeUnit.MILLISECONDS);//if timeout >1000, result will be true, because t1 released the lock after 1000ms

System.out.println("tryLock returns: "+res+" ,and "+Thread.currentThread().getName()+" is "+Thread.currentThread().getState());

} catch (InterruptedException e) { }

});

t1.start();

t2.start();

}

public static void main(String[] args) {

lock_tryLock_API();

lockInterruptibly_tryLock_API();

}

}

lockInterruptibly_tryLock_API

尝试打断interruptibleThread,输出如下

//output when interrupt interruptibleThread thread

Thread-0 was interrupted while getting lock

尝试打断uninterruptibleThread,输出如下

//output when interrupt uninterruptibleThread thread

ReentrantLock is locked by Thread-1

ReentrantLock is released by Thread-1

lock_tryLock_API

调整tryLock的时间,可以观察到给定足够尝试时间的情况下才能获取到锁

//output when tryLock time is NOT enough

Try to get the lock, if not, wait for sometime

ReentrantLock is locked by Thread-0

tryLock returns: false ,and Thread-1 is RUNNABLE

ReentrantLock is released by Thread-0

//output when tryLock time is enough

ReentrantLock is locked by Thread-0

Try to get the lock, if not, wait for sometime

ReentrantLock is released by Thread-0

tryLock returns: true ,and Thread-1 is RUNNABLE

使用tryLock解决死锁问题

一个典型的死锁案例,两个对象锁分别被两个线程持有,两个线程经过一段时间后各请求对方所持有的锁,自然就产生了死锁

public class DeadLock {

public static final Object lock1 = new Object();

public static final Object lock2 = new Object();

public static void main(String[] a) {

Thread t1 = new Thread1();

Thread t2 = new Thread2();

t1.start();

t2.start();

}

private static class Thread1 extends Thread {

public void run() {

synchronized (lock1) {

try {

Thread.sleep(10);

} catch (InterruptedException ignored) {}

synchronized (lock2) {

}

}

}

}

private static class Thread2 extends Thread {

public void run() {

synchronized (lock2) {

try {

Thread.sleep(10);

} catch (InterruptedException ignored) {}

synchronized (lock1) {

}

}

}

}

}

使用tryLock解决,我们的目的是使两个线程都能获取到两个锁,并且分别处理,那么我们可以把获取锁写在一个逻辑里

public class DeadLock {

public static final ReentrantLock reentrantLock1 = new ReentrantLock();

public static final ReentrantLock reentrantLock2 = new ReentrantLock();

private static boolean getLocks(long milliseconds){

boolean getLock1 = false, getLock2 = false;

try{

getLock1 = reentrantLock1.tryLock(milliseconds, TimeUnit.MILLISECONDS);

getLock2 = reentrantLock2.tryLock(milliseconds,TimeUnit.MILLISECONDS);

}catch (Exception e){

e.printStackTrace();

} finally {

if(getLock1 && getLock2) return true;

else if(getLock1) reentrantLock1.unlock();

else if(getLock2) reentrantLock2.unlock();

return false;

}

}

public static void main(String[] a) {

Thread t1 = new Thread1();

Thread t2 = new Thread2();

t1.start();

t2.start();

}

private static class Thread1 extends Thread {

public void run() {

boolean res = getLocks(1000);

if(res){

System.out.println(Thread.currentThread().getName()+" Got lock1?"+reentrantLock1.isHeldByCurrentThread());

System.out.println(Thread.currentThread().getName()+" Got lock2?"+reentrantLock2.isHeldByCurrentThread());

try{

//..do sth. with locked resources

Thread.sleep(100);

}catch (Exception e){

e.printStackTrace();

} finally {

reentrantLock1.unlock();

reentrantLock2.unlock();

}

}

}

}

private static class Thread2 extends Thread {

public void run() {

boolean res = getLocks(1000);

if(res){

System.out.println(Thread.currentThread().getName()+" Got lock1?"+reentrantLock1.isHeldByCurrentThread());

System.out.println(Thread.currentThread().getName()+" Got lock2?"+reentrantLock2.isHeldByCurrentThread());

try{

//..do other things. with locked resources

Thread.sleep(100);

}catch (Exception e){

e.printStackTrace();

} finally {

reentrantLock1.unlock();

reentrantLock2.unlock();

}

}

}

}

}

我们模拟两个线程,可以看到输出中,两个线程分别获得了两个锁,从而解决了死锁的问题

//output

Thread-1 Got lock1?true

Thread-1 Got lock2?true

Thread-0 Got lock1?true

Thread-0 Got lock2?true

Condition

Condition对应一个ReentrantLock,在调用时,要求线程持有该ReentrantLock的锁,我们看下Condition类的实例方法:

API

方法名

参数

返回

await

void

awaitUninterruptibly

void

awaitNanos

long

void

await

long,TimeUnit

boolean

awaitUntil

Date

boolean

signal

void

signalAll

void

可以看到,实际上主要实现了await和signal这两个功能,await和Object.await类似,给定等待时间,直到被通知,signal和notify类似,signal()唤醒等待在该Condition的线程,signalAll()唤醒所有等待在该Condition的线程

ReentrantLock Condition Example

两个方法分别对应两个示例

第一个示例中,一个线程通过condition.signalAll唤醒另一个线程,相应的输出和使用方法与synchronized下的Object.notify,Object.await完全一致

第二个示例中,前3个线程对应一个锁的ConditionA,后3个线程对应同一个锁的ConditionB,最后一个线程唤醒前3个线程,后3个线程在等待时间截止后自动执行,从而实现了线程的分组控制

public class ReentrantLockConditionAPI {

public static void singleConditionForThreads(){

ReentrantLock lock = new ReentrantLock();

Condition condition = lock.newCondition();

Thread t1 = new Thread(){

@Override

public void run() {

try {

lock.lock();

System.out.println(Thread.currentThread().getName()+" work for 1 second");

Thread.sleep(1000);

condition.await();//Must between lock() and unlock()

System.out.println("Finished, start to wait");

System.out.println(Thread.currentThread().getName()+" another work for 1 second");

Thread.sleep(1000);

}catch (Exception e){

e.printStackTrace();

}finally {

System.out.println(Thread.currentThread().getName() + " finished");

lock.unlock();

}

}

};

Thread t2 = new Thread(){

@Override

public void run() {

try {

Thread.sleep(100);

lock.lock();

System.out.println(Thread.currentThread().getName()+" got the lock");

condition.signalAll();

}catch (Exception e){

e.printStackTrace();

}finally {

System.out.println(Thread.currentThread().getName()+" finished");

lock.unlock();

}

}

};

t1.start();

t2.start();

}

public static void multipleConditionForThreadGroups(){

ReentrantLock lock = new ReentrantLock();

Condition conditionA = lock.newCondition();

Condition conditionB = lock.newCondition();

ExecutorService executorService = Executors.newFixedThreadPool(7);

//conditionA

for (int i = 0; i < 3; i++) {

executorService.submit(new Thread(){

@Override

public void run() {

try {

lock.lock();

conditionA.await(2000,TimeUnit.MILLISECONDS);

System.out.println(Thread.currentThread().getName()+" got the lock again");

} catch (InterruptedException e) {

e.printStackTrace();

}finally {

lock.unlock();

}

}

});

}

//conditionB

for (int i = 0; i < 3; i++) {

executorService.submit(new Thread(){

@Override

public void run() {

lock.lock();

try {

conditionB.await(2000, TimeUnit.MILLISECONDS);

System.out.println(Thread.currentThread().getName()+" got the lock again");

} catch (InterruptedException e) {

e.printStackTrace();

}finally {

lock.unlock();

}

}

});

}

executorService.submit(new Thread(){

@Override

public void run() {

try {

Thread.sleep(1000);

lock.lock();

conditionA.signalAll();

System.out.println(Thread.currentThread().getName()+" signalAll conditionA threads");

} catch (InterruptedException e) {

e.printStackTrace();

}finally {

lock.unlock();

}

}

});

executorService.shutdown();

}

public static void main(String[] args) {

singleConditionForThreads();

multipleConditionForThreadGroups();

}

}

第二个示例的输出(注意时间间隔)

//output

pool-1-thread-7 signalAll conditionA threads

pool-1-thread-1 got the lock again

pool-1-thread-2 got the lock again

pool-1-thread-3 got the lock again

pool-1-thread-4 got the lock again

pool-1-thread-5 got the lock again

pool-1-thread-6 got the lock again

Semaphore

Semaphore提供了信号量机制,作为mutex(互斥量)与ReentrantLock类似,不同的是,ReentrantLock中的Condition要求线程必须持有锁,并且一个锁只能被一个线程持有,而Semaphore允许多线程访问同一资源。当Semaphore不允许访问资源时,线程会被阻塞直到可以获取permit

API

方法

解释

Semaphore(int permits, boolean fair)

构造方法,两个参数分别表示permit数量,是否公平锁,默认非公平锁

acquire()

获取permit,否则阻塞直到可以获取

release()

释放permit,注意这里可以超过构造方法中允许的permit数量,也就是可以不用acquire直接release

availablePermits()

查看目前可用的permit

drainPermits()

把permit置0,返回所有permit的数量

与其他锁机制的对比

Semaphore不关心获取锁和释放锁的对象,并且允许多线程同时访问同一资源,acquire和release只是发放和收回permit(许可),是一种相对高级的同步机制

Semaphore Example

因为是高级API,所以没啥特别的,示例中一个容量为5的信号量跑20个线程,可以防止死锁,互斥执行

public class SemaphoreAPI {

public static void main(String[] args) {

Semaphore semaphore = new Semaphore(5);

ExecutorService executorService = Executors.newFixedThreadPool(20);

for (int i = 0; i < 20; i++) {

executorService.submit(()->{

try {

semaphore.acquire();

System.out.println(Thread.currentThread().getName()+" is doing some work");

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}finally {

semaphore.release();

}

});

}

executorService.shutdown();

}

}

refs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值