Java控制线程顺序执行的常用方法汇总
一,Jdk 线程的join()方法
按需要执行的顺序依次加入即可,简单也比较常用
public class Test {
public static void main(String[] args) {
Thread threadA = new Thread("A") {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("我是线程A执行");
}
};
Thread threadB = new Thread("B") {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("我是线程B执行");
}
};
Thread threadC = new Thread("C") {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("我是线程C执行");
}
};
try {
threadA.start();
threadA.join();
threadB.start();
threadB.join();
threadC.start();
threadC.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("main线程执行完毕");
}
}
二使用线程池创建单一的线程去实现
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.print("runnable a 执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.print("runnable b 执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.print("runnable c 执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.shutdown();
}
三 object的 wait(),notify()方法,配合synchronized使用
public class Test3 {
private static volatile int STEP = 0;
public static void main(String[] args) {
Step step = new Step();
new Thread(new RunnableA(step)).start();
new Thread(new RunnableB(step)).start();
new Thread(new RunnableC(step)).start();
}
public static class RunnableA implements Runnable {
private Step step;
private RunnableA(Step step) {
this.step = step;
}
@Override
public void run() {
step.excuteA();
}
}
public static class RunnableB implements Runnable {
private Step step;
private RunnableB(Step step) {
this.step = step;
}
@Override
public void run() {
step.excuteB();
}
}
public static class RunnableC implements Runnable {
private Step step;
private RunnableC(Step step) {
this.step = step;
}
@Override
public void run() {
step.excuteC();
}
}
public static class Step {
public void excuteA() {
synchronized (this) {
try {
System.out.println("线程A开始执行");
while (STEP != 0) { // 用while if唤醒后会直接往下执行
System.out.println("STEP" + STEP);
wait();
}
Thread.sleep(3000);//模拟延迟//休眠不会释放锁 ,需要放到wait后面
STEP = 1;
System.out.println("线程A执行完毕");
notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void excuteB() {
synchronized (this) {
try {
while (STEP != 1) {
wait();
}
System.out.println("线程B开始执行");
Thread.sleep(3000);
STEP = 2;
System.out.println("线程B执行完毕");
notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void excuteC() {
synchronized (this) {
try {
while (STEP != 2) {
wait();
}
System.out.println("线程c开始执行");
Thread.sleep(3000);
System.out.println("线程C执行完毕");
notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
四 CountDownLatch
CountDownLatch是JAVA提供在并发包下的一个辅助类,可以把它看成是一个计数器,其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器,CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的cutDown()方法,来使计数减1;如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过cutDown方法,将计数减到0,才可以继续执行。
public class Test4 {
private static final int[] lock = new int[1];
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(3);
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
while (countDownLatch.getCount() != 3) {
lock.wait();// 必须用锁对象的wait()
}
Thread.sleep(3000);//模拟延迟
System.out.println("线程A执行完毕");
countDownLatch.countDown();
lock.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
while (countDownLatch.getCount() != 2) {
lock.wait();
}
Thread.sleep(3000);//模拟延迟
System.out.println("线程B执行完毕");
countDownLatch.countDown();
lock.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
while (countDownLatch.getCount() != 1) {
lock.wait();
}
Thread.sleep(3000);//模拟延迟
System.out.println("线程C执行完毕");
countDownLatch.countDown();
lock.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
try {
countDownLatch.await();
System.out.print("main执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
五 CyclicBarrier
栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生。栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。 CyclicBarrier可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,而栅栏将被重置以便下次使用。
public class Test5 {
public static void main(String[] args) {
final CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
final CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程A开始执行任务");
Thread.sleep(5000);
System.out.println("线程A执行完毕");
cyclicBarrier1.await();//阻塞等待所有的线程到达
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier1.await();//1号栅栏解除阻塞
System.out.println("线程B开始执行");
Thread.sleep(5000);
System.out.println("线程B执行完毕");
cyclicBarrier2.await();//2号阻塞等待所有的线程到达
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier2.await();//2号栅栏解除阻塞
System.out.println("线程c开始执行");
Thread.sleep(5000);
System.out.println("线程c执行完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
六 ReentrantLock
配合自定义执行条件 原理同wait() notify()
public class Test7 {
private volatile static int step = 1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition conditionA = lock.newCondition();
public static void main(String[] args) {
Thread threadA = new Thread() {
public void run() {
try {
lock.lock();
while (step != 1) {
conditionA.await();
}
System.out.println("A线程开始执行");
Thread.sleep(3000);
step = 2;
System.out.println("A线程执行完毕");
conditionA.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadB = new Thread() {
public void run() {
try {
lock.lock();
while (step != 2) {
conditionA.await();
}
System.out.println("B线程开始执行");
Thread.sleep(3000);
step = 3;
System.out.println("B线程执行完毕");
conditionA.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadC = new Thread() {
public void run() {
try {
lock.lock();
while (step != 3) {
conditionA.await();
}
System.out.println("c线程开始执行");
Thread.sleep(3000);
step = 1;
System.out.println("c线程执行完毕");
conditionA.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
threadA.start();
threadB.start();
threadC.start();
}
}
七 synchronized
配合wait(),notify()+自定义执行条件,原理同上wait notify
八 Semaphore(信号量)
Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。
acquire()
获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
acquire(int permits)
获取一个令牌,在获取到令牌、或者被其他线程调用中断、或超时之前线程一直处于阻塞状态。
acquireUninterruptibly() 获取一个令牌,在获取到令牌之前线程一直处于阻塞状态(忽略中断)。
tryAcquire() 尝试获得令牌,返回获取令牌成功或失败,不阻塞线程。
tryAcquire(long timeout, TimeUnit unit) 尝试获得令牌,在超时时间内循环尝试获取,直到尝试获取成功或超时返回,不阻塞线程。
release() 释放一个令牌,唤醒一个获取令牌不成功的阻塞线程
public class Test6 {
public static void main(String[] args) {
final Semaphore semaphore1 = new Semaphore(0);//一开始就使线程阻塞从而让其他线程顺序执行。
final Semaphore semaphore2 = new Semaphore(0);
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore1.acquire();
System.out.println("线程B开始执行");
Thread.sleep(5000);
System.out.println("线程B执行完毕");
semaphore1.release();
semaphore2.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
final Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore2.acquire();//暂时无法获取 等待线程A执行完毕后释放 保证了线程执行顺序
System.out.println("线程c开始执行");
Thread.sleep(5000);
System.out.println("线程c执行完毕");
semaphore2.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程A开始执行");
Thread.sleep(5000);
System.out.println("线程A执行完毕");
thread2.start();//顺序执行B C
thread3.start();
semaphore1.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();//先执行线程A
}
}