问题:
开始三个线程a,b,c,现在bc需要获取a的数据
思路:
如果需要bc需要得到a的数据,首先需要a本身有数据,所以必须a进程先运行,然后运行bc。需使用Semaphore
Semaphore介绍
Semaphore [ˈseməfɔ:®] ,计数信号量,用于限制可以访问资源的线程数量,可以使用这个类来限制线程的访问。
构造方法如下:
Semaphore(int permits)
创建一个 Semaphore与给定数量的许可证和非公平公平设置。
Semaphore(int permits, boolean fair)
创建一个 Semaphore与给定数量的许可证和给定的公平设置。
其中许可证的意思由线程获取,线程获取到许可证后方可运行;公平设置指调用acquire方法的顺序就是获取许可证的顺序,而非公平设置是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证。
主要调用方法如下:
acquire()
从该信号量获取许可证,阻止直到可用,
release()
释放许可证,将其返回到信号量。
下面是Semaphore运用的例子
代码引用来自 https://blog.csdn.net/qq_19431333/article/details/70212663
public class Playground {
static class Track {
private int num;
public Track(int num) {
this.num = num;
}
public String toString() {
return "Track{" +
"num=" + num +
'}';
}
}
private Track[] tracks = {new Track(1),new Track(2),new Track(3)};
//volatile 属性强制线程访问主存
private volatile boolean[] used = new boolean[3];
// 第一个参数许可证数量,第二个参数是否是公平模式
// 公平模式下调用acquire的顺序就是获取许可证的顺序
// 而非公平模式是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证
public Semaphore semphore = new Semaphore(3, true);
// 获取跑道
public Track getTrack() throws InterruptedException {
semphore.acquire(); // 获取许可证,阻止直到 “所有”可用
return getNextAvailableTrack();
}
// 返回一个跑到
public void releaseTrack(Track track) {
if(makeAsUsed(track))
semphore.release(); // 释放许可证,返回给信号量
}
/**
* 返回一个跑道
* @param track
* @return
*/
private boolean makeAsUsed(Track track) {
for (int i = 0; i < used.length; i++) {
if (tracks[i] == track) {
if (used[i]) {
used[i] = false;
return true;
} else {
return false;
}
}
}
return false;
}
/**
* 查找没人使用的跑道
* @return
*/
private Track getNextAvailableTrack() {
for(int i=0;i<used.length;i++) {
if(!used[i]) {
used[1] = true;
return tracks[i];
}
}
return null;
}
}
测试类
public class SemaphoreTest {
static class Student implements Runnable{
private int num;
private Playground playground;
public Student(int num, Playground playground) {
this.num = num;
this.playground = playground;
}
@Override
public void run() {
try {
Playground.Track track = playground.getTrack();
if(track != null) {
System.out.println("学生" + num + "在" + track.toString() + "上跑步");
TimeUnit.SECONDS.sleep(2); // TimeUnit指定时间粒度单位上持续时间,sleep表示睡眠操作
System.out.println("学生" + num + "释放" + track.toString()); //释放跑道
playground.releaseTrack(track);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Executor executor = Executors.newCachedThreadPool();
Playground playground = new Playground();
for (int i = 0; i < 100; i++) {
executor.execute(new Student(i+1,playground));
}
}
}
了解完Semaphore后开始使用解决问题
未解决问题前代码
public class ThreadCommunication {
private static int num;//定义一个变量作为数据
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
//模拟耗时操作之后初始化变量 num
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"获取到 num 的值为: "+num);
}
});
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"获取到 num 的值为: "+num);
}
});
//同时开启 3 个线程
threadA.start();
threadB.start();
threadC.start();
}
}
解决后代码:
/**
* 定义一个信号量,该类内部维持了多个线程锁,可以阻塞多个线程,释放多个线程,
* 线程的阻塞和释放是通过 permit 概念来实现的
* 线程通过 semaphore.acquire()方法获取 permit,如果当前 semaphore 有 permit 则分配给该线程,
* 如果没有则阻塞该线程直到 semaphore
* 调用 release()方法释放 permit。
* 构造函数中参数: permit(允许) 个数
*
*/
public class ThreadCommunication2 {
private static int num;//定义一个变量作为数据
// 创建一个 Semaphore与给定数量的许可证和非公平公平设置
// 这里设置在线程A运行后再给出许可证
private static Semaphore semaphore = new Semaphore(0);
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
//模拟耗时操作之后初始化变量 num
try {
Thread.sleep(1000);
num = 1;
// 释放给定数量的许可证,将其返回到信号量
semaphore.release(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"获取到 num 的值为: "+num);
}
});
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"获取到 num 的值为: "+num);
}
});
//同时开启 3 个线程
threadA.start();
threadB.start();
threadC.start();
}
}