在java多线程,不免会遇到我们需要控制线程执行顺序的情况。比较经典的情况就是生产者+消费者模式,只有当生产者被执行了,消费者才能被执行,否则就就可能陷入死锁。
首先介绍简单的实现消费者模式的方法:
消费者-生产者
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue == true) {
condition.await();
}
System.out.println("###");
hasValue = true;
condition.signal();
get();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
condition.await();
}
System.out.println("***");
hasValue = false;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
//线程A
public class ThreadA extends Thread{
private MyService myService;
public ThreadA(MyService myService){
this.myService = myService;
}
public void run(){
for(int i=0;i<Integer.MAX_VALUE;i++){
myService.set();
}
}
}
//线程B
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
//运行类
public class runTest {
public static void main(String [] args){
MyService myServie = new MyService();
ThreadA a = new ThreadA(myServie);
a.start();
ThreadA b = new ThreadA(myServie);
b.start();
}
}
运行结果如图:
当然,也可以通过lock,synchronized来实现,代码比较简单,就不贴出来了。
这里,在贴一个比较复杂的生产者消费者的例子:这个示例可以实现多个生产与消费者,利用lock与synchronized来进行实现。(代码比较多,只贴了核心的部分,全部代码请下载)
public class P {
private String lock;
public P(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
if (Value.value.equals("")) {
System.out.println(Thread.currentThread().getName()+" wait... PPPPPP");
lock.wait();
}
System.out.println(Thread.currentThread().getName()+" the value of get "+Value.value);
Value.value ="";
lock.notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
生产者的实现部分
public class C {
private String lock;
public C(String lock){
this.lock = lock;
}
public void getValue(){
try{
synchronized(lock){
if(!Value.value.equals("")){
System.out.println(Thread.currentThread().getName()+" wait... CCCCCCC");
lock.wait();
}
System.out.println(Thread.currentThread().getName()+" set value --- CCCCCC");
String value = "current time is "+System.currentTimeMillis();
Value.value = value;
lock.notify();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
消费者的实现部分、
public class Runtest {
static int N = 2;
public static void main(String[] args) throws InterruptedException {
String lock = "chuchu";
P p = new P(lock);
C c = new C(lock);
ThreadP[] pThread = new ThreadP[N];
ThreadC[] cThread = new ThreadC[N];
for (int i = 0; i < N; i++) {
pThread[i] = new ThreadP(p);
pThread[i].setName("生产者"+(i+1));
cThread[i] = new ThreadC(c);
cThread[i].setName("消费者"+(i+1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
Thread[] array = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(array);
for(int i=0;i<array.length;i++){
System.err.println("============================"+array[i].getName()+" "+array[i].getState());
}
}
}
测试类的运行代码:
运行结果如图:
可以看到,在消费者一直在生产者的后面运行。结果基本符合预想。
利用join()方法控制
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
譬如你这么写,可以直接控制线程的运行顺序:
public class hahahha {
/**
* @param args
*/
public static void main(String[] args) {
t1.start();
t2.start();
t3.start();
t4.start();
}
static Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A");
}
});
static Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("B");
}
});
static Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("C");
}
});
static Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
try {
t3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("D");
}
});
}
运行结果就是按顺序输出ABCD,就不上图了。自行理解,不解释。
利用多重锁来实现控制线程的运行
public class testRun implements Runnable{
private String name;
private Object prev;
private Object self;
public testRun(String name, Object prev, Object self) {
super();
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run(){
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行类的代码如下:
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
Object c = new Object();
testRun p1 = new testRun("A",c,a);
testRun p2 = new testRun("B",a,b);
testRun p3 = new testRun("C",b,c);
new Thread(p1).start();
new Thread(p2).start();
new Thread(p3).start();
}
运行结果就是按顺序输出ABC,不行的话可以自己试一试。