java线程协作_java线程间的协作

1、为什么线程之间需要协作

线程之间相互配合,完成某项事情,好比:一个线程修改了一个工具的值,而另一个线程感知到了转变,然后举行响应的操作,整个历程开始于一个线程,而最终执行又是另一个线程。前者是生产者,后者就是消费者,这种模式隔离了“做什么”(What)和“怎么做”(How)。简朴的设施是让消费者线程不断地循环检查变量是否相符预期,在while循环中设置不知足的条件,若是条件知足则退出while循环,从而完成消费者的事情。这样举行线程之间的协作却存在如下2个问题:

(1)难以确保及时性。

(2)难以降低开销。若是降低睡眠的时间,好比休眠1毫秒,这样消费者能加倍迅速地发现条件转变,然则却可能消耗更多的处置器资源,造成了无故的虚耗。

那么有没有什么设施可以解决以上2个问题呢?此时守候/通知机制毫不客气的站出来说,都闪开,交给我,我能行!

2、守候/通知机制

2.1 守候/通知机制先容

守候/通知机制是指一个线程A挪用了工具obejct的wait()方式进入守候状态,而另一个线程B挪用了工具obejct的notify()或者notifyAll()方式,线程A收到通知后从工具obejct的wait()方式返回,进而执行后续操作。上述两个线程通过工具obejct来完成交互,而工具上的wait()和notify/notifyAll()的关系就犹如开关信号一样,用来完成守候方和通知方之间的交互事情。

notify():

通知一个在工具上守候的线程,使其从wait()方式返回,而返回的条件是该线程获取到了工具的锁。哪个线程能获得通知是随机的,不能指定。

notifyAll():

通知所有守候在该工具上的线程,这些线程会去竞争工具锁,获得锁的某一个线程可以继续执行wait()后的逻辑。

wait():

挪用该方式的线程进入WAITING状态,只有守候另外线程的通知或被中止才会返回。需要注重,挪用wait()方式后,会释放工具的锁。

wait(long):

超时守候一段时间,这里的参数时间是毫秒,也就是守候长达n毫秒,若是没有通知就超时返回。

wait (long,int):

对于超时时间更细粒度的控制,可以到达纳秒。

2.1 守候/通知机制使用的尺度范式

守候方遵照如下原则:

(1)获取工具的锁。

(2)若是条件不知足,那么挪用工具的wait()方式,被通知后仍要检查条件。

(3)条件知足则执行对应的逻辑。

用一段伪代码示意:

synchronized(工具) {while(条件不知足) {

工具.wait();

}

对应逻辑处置

}

通知方遵照如下原则:

(1)获得工具的锁。

(2)改变条件。

(3)通知所有守候在工具上的线程。

(4)通知方式放在同步代码块的最后一行。

用一段伪代码示意:

synchronized(工具) {

改变条件

工具.notifyAll();

}

在挪用wait()、notify()和notifyAll()方式之前,线程必须要获得该工具的工具锁,即只能在同步方式或同步块中挪用wait()方式、notify()和notifyAll()方式。挪用wait()方式后,当前线程释放锁, 执行notify()和notifyAll()方式的线程退出synchronized代码块的时刻,假设是执行的notifyAll(),会叫醒所有处于守候的线程,这些线程会去竞争工具锁。若是其中一个线程A获得了该工具锁,线程A就会继续往下执行,其余被叫醒的线程处于阻塞状态。在线程A退出synchronized代码块释放锁后,其余已经被叫醒的处于阻塞状态的线程将会继续竞争该锁,一直举行下去,直到所有被叫醒的线程都执行完毕。

2.2notify()和notifyAll()应该用谁

尽可能用notifyAll(),郑重使用notify(),由于notify()只会叫醒一个线程,我们无法确保被叫醒的这个线程一定就是我们需要叫醒的线程。

2.3 手写一个数据库毗邻池

我们在使用数据库毗邻池的时刻,当某一个线程跨越设置的最大守候时长还没有拿到毗邻时,就会报出异常。我们使用守候/通知机制来模拟一个数据库毗邻池。划分界说毗邻类、毗邻池实现类和测试类。

毗邻类:

import java.sql.*;importjava.util.Map;importjava.util.Properties;importjava.util.concurrent.Executor;public class MySqlConnection implementsConnection {public static finalConnection createConnection(){return newMySqlConnection();

}//todo 其余接口使用默认实现,这里就不逐一给出。

}

毗邻池实现类:

importjava.sql.Connection;importjava.util.LinkedList;public classMyConnectionPool {/**装毗邻的容器*/

private static LinkedList pool = new LinkedList<>();/*** 初始化毗邻池

*@parampoolSize*/

public MyConnectionPool(intpoolSize){if(poolSize > 0){for (int i = 0; i < poolSize; i++) {

pool.add(MySqlConnection.createConnection());

}

}

}/*** 释放一个毗邻

*@paramconnection*/

public voidreleaseConnection(Connection connection){if(connection != null){synchronized(pool){

pool.add(connection);

pool.notifyAll();

}

}

}/*** 获取一个毗邻

*@parammillions 超时时间

*@return

*/

public Connection getConnection(longmillions){synchronized(pool){if(millions <= 0){while(pool.isEmpty()){try{

pool.wait();

}catch(InterruptedException e){

e.printStackTrace();

}

}returnpool.removeFirst();

}else{//盘算超时时刻

long overTime = System.currentTimeMillis() +millions;//剩余守候时长

long remaining =millions;//当剩余守候时间大于0而且毗邻池为空,就守候

while (remaining > 0 &&pool.isEmpty()){try{

pool.wait();

}catch(InterruptedException e){

e.printStackTrace();

}//被叫醒后重新盘算剩余守候时长

remaining = overTime -System.currentTimeMillis();

}

Connection result= null;if(!pool.isEmpty()){

result=pool.removeFirst();

}returnresult;

}

}

}

}

测试类:

importjava.sql.Connection;importjava.sql.Statement;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.atomic.AtomicInteger;public classMyConnectionPoolTest {/**初始化10个数据库毗邻*/

static MyConnectionPool pool = new MyConnectionPool(10);/**守候时长为1秒*/

static long maxWait = 1000;/**获取毗邻的线程数目*/

static int threadNumber = 500;/**所有获取毗邻线程执行完成后再执行main线程*/

static CountDownLatch mainCountDownLatch = newCountDownLatch(threadNumber);/**保证所有获取毗邻线程同时执行*/

static CountDownLatch workCountDownLatch = newCountDownLatch(threadNumber);static class Worker implementsRunnable{

AtomicInteger success;

AtomicInteger fail;publicWorker(AtomicInteger success, AtomicInteger fail){this.success =success;this.fail =fail;

}

@Overridepublic voidrun() {try{

workCountDownLatch.await();

Connection connection=pool.getConnection(maxWait);if(connection != null){try{

Statement statement=connection.createStatement();

Thread.sleep(30);//休眠30毫秒模拟现实营业

connection.commit();

}finally{

pool.releaseConnection(connection);

success.getAndAdd(1);

}

}else{

fail.getAndAdd(1);

System.out.println(Thread.currentThread().getName()+ "守候超时,没有拿到毗邻!");

}

}catch(Exception e){

e.printStackTrace();

}

mainCountDownLatch.countDown();

}

}public static voidmain(String[] args) {

AtomicInteger success= new AtomicInteger(0);//纪录乐成次数

AtomicInteger fail = new AtomicInteger(0);//纪录失败次数

for (int i = 0; i < threadNumber; i++) {new Thread(newWorker(success, fail)).start();

workCountDownLatch.countDown();

}try{

mainCountDownLatch.await();

}catch(InterruptedException e){

e.printStackTrace();

}

System.out.println("总共实验获取毗邻次数:" +threadNumber);

System.out.println("乐成次数:" +success.get());

System.out.println("失败次数:" +fail.get());

}

}

运行程序,从输出效果可以看出,通过通知/守候超时模式乐成的实现了一个浅易的数据库毗邻池。

b19adb108c924d552b2f7518bdca4d74.png

2.4 常见面试题

挪用yield() 、sleep()、wait()、notify()等方式对锁有何影响?

答:yield() 、sleep()被挪用后,都不会释放当前线程所持有的锁。

挪用wait()方式后,会释放当前线程持有的锁,而且当前线程被叫醒后,会重新去竞争锁,获得锁到后才会执行wait()方式后面的代码。

挪用notify()系列方式后,对锁无影响,线程只有在synchronized同步代码执行完后才会自然而然的释放锁,以是notify()系列方式一样平常都是synchronized同步代码的最后一行。

线程的并发工具类在下一篇文章中先容,在阅读历程中如发现形貌有误,请指出,谢谢。

原文链接:https://www.cnblogs.com/hongshaodian/p/12448653.html

本站声明:网站内容来源于网络,若有侵权,请联系我们,我们将及时处置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值