信号量
如果学过操作系统这门课应该对信号量这个名词相当熟悉,其中Mutex互斥锁更是操作系统中用来访问临界资源的用例经典。
其实包括p操作和v操作
void p(s){
while(s<=0);
s--;
}
void V(s){
s++;
}
倒计时门栓
让一个线程集等待直到计数变为0,倒计时门栓是一次性的。一旦计数器为0,就不能再用了
当一个或者 多个线程需要等待直到指定数目的事件发生时使用。
一个人要完成一些任务,但是需要些数据,此时这个人准备好后(就绪)在门外等着,另外几个人在里面准备数据。这个人数取决于new门栓是的参数,当数据准备好后调用countdown(),那个要完成要任务的就可以开始工作啦、跟下面的障栅是几乎一样的。所以看下面就应该能明白我要说的了。
障栅
很形象的名字,可以把线程比喻成一队运动员,当最先一个运动员跑到终点时先等待后边的。当所有运动员都跑到这个障栅时,然后再一起跑。期间如果任何一个线程在等待时候离开的障栅,会破坏障栅,其它线程会报异常错误。
书上说使用场景 是当大量的线程需要在它们结果可用之前完成时。
package test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Phaser;
public class Demo {
private static int num = 0;
public static void main(String[] Args) {
new Demo().test();
}
private void test() {
Runnable comtask = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("完成啦完成啦");
}
};
CyclicBarrier cb = new CyclicBarrier(3, comtask);
thread t1 = new thread(cb);
thread t2 = new thread(cb);
thread t3 = new thread(cb);
t1.start();
t2.start();
t3.start();
System.out.println(cb.getNumberWaiting());
}
class thread extends Thread {
private CyclicBarrier cb;
public thread(CyclicBarrier cb) {
super();
this.cb = cb;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
cb.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出结果:
完成啦完成啦
0
0
障栅是可以重用的,之后调用 cb.reset();即可完成重置,而倒计时门栓只可以用一次。
交换器
交换嘛,两个线程在同一个缓冲区的两个实例上工作的时候,就可以用交换器,如,一个人在做饭,一个人在吃饭。那么这个碗就是缓冲区,做完了,吃完了。之后交换这个碗。就是一个线程在消耗数据,一个在准备数据。又比如一个线程在根据数据进行计算,一个线程在收集新的数据。
package test;
import java.util.concurrent.Exchanger;
public class Demo {
private static int num = 0;
public static void main(String[] Args) {
new Demo().test();
}
private void test() {
Exchanger<Integer> ec = new Exchanger<Integer>();
threadcook tc = new threadcook(ec);
threadeat te = new threadeat(ec);
tc.start();
te.start();
}
class threadcook extends Thread {
private Exchanger<Integer> ec;
public threadcook(Exchanger<Integer> ec) {
super();
this.ec = ec;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
int result = 0;
for (int i = 0; i < 100; i++) {
result += i;
}
try {
ec.exchange(result);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("我是做饭的内个~:" + result);
}
}
}
class threadeat extends Thread {
private Exchanger<Integer> ec;
public threadeat(Exchanger<Integer> ec) {
super();
this.ec = ec;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
int result = 0;
try {
ec.exchange(result);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("我是吃饭的内个~:" + result);
}
}
}
}
结果:
我是做饭的内个~:4950
我是吃饭的内个~:0
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是吃饭的内个~:0
我是做饭的内个~:4950
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是吃饭的内个~:0
我是做饭的内个~:4950
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
我是吃饭的内个~:0
我是做饭的内个~:4950
好吧要吃饭的那个估计已经饿死了。。。那么问题出在哪呢。。
哦这个点找错了,交换之后获得的对象是exchange()这个方法的返回值,并不会重新对result赋值。所以改过之后
package test;
import java.util.concurrent.Exchanger;
import javax.xml.ws.spi.Invoker;
public class Demo {
private static int num = 0;
public static void main(String[] Args) {
new Demo().test();
}
private void test() {
Exchanger<Integer> ec = new Exchanger<Integer>();
threadcook tc = new threadcook(ec);
threadeat te = new threadeat(ec);
tc.start();
te.start();
}
class threadcook extends Thread {
private Exchanger<Integer> ec;
public threadcook(Exchanger<Integer> ec) {
super();
this.ec = ec;
}
@Override
public void run() {
// TODO Auto-generated method stub
int result = 0;
for (int i = 0; i < 100; i++) {
result += i;
}
try {
System.out.println("我是做饭的内个~:" + ec.exchange(result));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class threadeat extends Thread {
private Exchanger<Integer> ec;
public threadeat(Exchanger<Integer> ec) {
super();
this.ec = ec;
}
@Override
public void run() {
// TODO Auto-generated method stub
int result = 0;
try {
System.out.println("我是吃饭的内个~:" + ec.exchange(result));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
结果 :
我是吃饭的内个~:4950
我是做饭的内个~:0
这就对 了~~
同步队列
跟交换器很像,不过这次是单向的,一个线程在向队列里边压数据 ,另一个在取,不过这个交换器的不同就是这个可以是多个线程之间的协作,不像交换器只能支持两个线程。
为了方便我们将上一个例子改一下就好了啦。
为了体现出是队列,我们取100次却只压入10次队列。
package test;
import java.util.concurrent.Exchanger;
import java.util.concurrent.SynchronousQueue;
import javax.xml.ws.spi.Invoker;
public class Demo {
private static int num = 0;
public static void main(String[] Args) {
new Demo().test();
}
private void test() {
SynchronousQueue<Integer> sq = new SynchronousQueue<Integer>();
threadcook tc = new threadcook(sq);
threadeat te = new threadeat(sq);
tc.start();
te.start();
}
class threadcook extends Thread {
private SynchronousQueue<Integer> sq;
public threadcook(SynchronousQueue<Integer> sq) {
super();
this.sq = sq;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
try {
System.out.println(sq.take());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class threadeat extends Thread {
private SynchronousQueue<Integer> sq;
public threadeat(SynchronousQueue<Integer> sq) {
super();
this.sq = sq;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
try {
sq.put(i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
结果:
0
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
喵~跟预计的一样。好的呢,今天就到这里了。