Java并发编程系列学习_Semaphore并发控制器&Exchanger数据交换器

Semaphore并发资源控制器的使用场景

Semaphore可以理解为信号量用于控制资源能够被并发访问的线程数量,以保证多个线程能够合理的使用特定资源。Semaphore就相当于一个许可证,线程需要先通过acquire方法获取该许可证,该线程才能继续往下执行,否则只能在该方法出阻塞等待。当执行完业务功能后,需要通过release()方法将许可证归还,以便其他线程能够获得许可证继续执行。

Semaphore可以用于做流量控制,特别是公共资源有限的应用场景,比如数据库连接。假如有多个线程读取数据后,需要将数据保存在数据库中,而可用的最大数据库连接只有10个,这时候就需要使用Semaphore来控制能够并发访问到数据库连接资源的线程个数最多只有10个。在限制资源使用的应用场景下,Semaphore是特别合适的。

package semaphore;
import java.util.concurrent.Semaphore;

/**
 * @Author: jiaqing.xu@hand-china.com
 * @Date: 2019-03-29 14:28
 * @Description
 */
public class SemaphoreService {
    /**
     * 最多只有一个信号量
     * 同一个时间内最多允许1个线程执行acquire和release之间的代码
     * 如果该值大于1并不能保证线程的安全性 有可能出现多个线程共同访问实例变量的情况
     */
    private Semaphore semaphore = new Semaphore(1);
    /**
     * 测试方法
     */
    public void testMethod() throws InterruptedException {
        //获取信号量 参数值 代表许可的数量 这里代表调用一次会获取到1个许可
        semaphore.acquire();
        System.out.println("The begin thread:" + Thread.currentThread().getName() + " time:" + System.currentTimeMillis());
        Thread.sleep(1000);
        System.out.println("The end thread:" + Thread.currentThread().getName() + " time:" + System.currentTimeMillis());
        //释放信号量
        semaphore.release();
    }
}
package semaphore;

/**
 * @Author: jiaqing.xu@hand-china.com
 * @Date: 2019-03-29 14:33
 * @Description
 */
public class TestSemaphore {
    public static void main(String[] args) throws InterruptedException {

        SemaphoreService semaphoreService = new SemaphoreService();
        //1.继承Thread
        Thread thread1 = new Thread(() -> {
            try {
                semaphoreService.testMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.setName("a");

        //2.继承Thread
        Thread thread2 = new Thread(() -> {
            try {
                semaphoreService.testMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread2.setName("b");
        thread1.start();
        thread2.start();
    }
}

执行结果如下图所示:

下面来看下Semaphore的主要方法:

//获取许可,如果无法获取到,则阻塞等待直至能够获取为止
void acquire() throws InterruptedException 

//同acquire方法功能基本一样,只不过该方法可以一次获取多个许可
void acquire(int permits) throws InterruptedException

//释放许可
void release()

//释放指定个数的许可
void release(int permits)

//尝试获取许可,如果能够获取成功则立即返回true,否则,则返回false
boolean tryAcquire()

//与tryAcquire方法一致,只不过这里可以指定获取多个许可
boolean tryAcquire(int permits)

//尝试获取许可,如果能够立即获取到或者在指定时间内能够获取到,则返回true,否则返回false
boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException

//与上一个方法一致,只不过这里能够获取多个许可
boolean tryAcquire(int permits, long timeout, TimeUnit unit)

//返回当前可用的许可证个数
int availablePermits()

//返回正在等待获取许可证的线程数
int getQueueLength()

//是否有线程正在等待获取许可证
boolean hasQueuedThreads()

//获取所有正在等待许可的线程集合
Collection<Thread> getQueuedThreads()

另外,在Semaphore的构造方法中还支持指定是够具有公平性,默认的是非公平性,这样也是为了保证吞吐量。

Exchanger线程数据交换器的使用场景

先看下这个例子,了解下exchanger的简单使用:

ThreadA的定义如下:

package extthread;
import java.util.concurrent.Exchanger;

/**
 * @Author: jiaqing.xu@hand-china.com
 * @Date: 2019-03-29 16:03
 * @Description
 */
public class ThreadA extends Thread {

    private Exchanger<String> exchanger;

    public ThreadA(Exchanger<String> exchanger) {
        super();
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        try {
            System.out.println("在线程A中获取的线程B的值=" + exchanger.exchange("1"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

ThreadB的定义如下:

package extthread;
import java.util.concurrent.Exchanger;

/**
 * @Author: jiaqing.xu@hand-china.com
 * @Date: 2019-03-29 16:08
 * @Description
 */
public class ThreadB extends Thread {

    private Exchanger<String> exchanger;

    public ThreadB(Exchanger<String> exchanger) {
        super();
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        try {
            System.out.println("在线程B中获取的线程A的值=" + exchanger.exchange("2"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
package extthread;
import java.util.concurrent.Exchanger;

/**
 * @Author: jiaqing.xu@hand-china.com
 * @Date: 2019-03-29 16:10
 * @Description
 */
public class Main {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();
        ThreadA threadA = new ThreadA(exchanger);
        ThreadB threadB = new ThreadB(exchanger);
        //启动a b线程
        threadA.start();
        threadB.start();
    }
}
在线程B中获取的线程A的值=1
在线程A中获取的线程B的值=2

Process finished with exit code 0

Exchanger是一个用于线程间协作的工具类,用于两个线程间数据能够交换。它提供了一个交换的同步点,在这个同步点两个线程能够交换数据。具体交换数据是通过exchange方法来实现的,如果一个线程先执行exchange方法,那么它会同步等待另一个线程也执行exchange方法,这个时候两个线程就都达到了同步点,两个线程就可以交换数据。

//当一个线程执行该方法的时候,会等待另一个线程也执行该方法,因此两个线程就都达到了同步点
//将数据交换给另一个线程,同时返回获取的数据
V exchange(V x) throws InterruptedException

//同上一个方法功能基本一样,只不过这个方法同步等待的时候,增加了超时时间
V exchange(V x, long timeout, TimeUnit unit)
    throws InterruptedException, TimeoutException 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值