`

Java多线程编程线程的协同、停止、暂停、继续等操作实现

 
阅读更多

      Java线程是Java执行的基本单元,单线程程序编写比较简单,但是处理效率不高。随着CPU核心数量的增加和对程序高性能的要求,多线程编程也成为必然的趋势。

      Java提供了至少三种实现线程的方式,一种是Runnable,一种是Thread,还有一种是线程池的方式。

1.Runnable方式:

Thread t = new Thread(new Runnable(){

public void run(){

//do something...

}

 

});

t.start();

2.Thread方式:

Thread t = new Thread(){

public void run(){

//do something...

}

}

t.start();

3.线程池的方式:

public class ThreadPoolEx {

private static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(100);

private static ThreadPoolExecutor executors = new ThreadPoolExecutor(10,10,1000,TimeUnit.SECONDS,queue);

 

public static void main(String[] args) {

 

for(int i = 0;i<20;i++){

executors.execute(new RunnableTask(i));

}

}

/**

* 执行单元

*/

public static class RunnableTask implements Runnable{

 

private int index;

 

public RunnableTask(int index){

this.index = index;

}

 

@Override

public void run() {

System.out.println(index + "线程执行");

}

 

}

 

}

多线程在执行过程中,需要比单线程考虑很多问题。比如说线程安全;线程协同工作;线程停止,暂停,继续等工作。

一、线程安全

      线程安全就是在多线程程序执行过程中,多个线程会同时访问修改同一变量(全局变量),会导致变量状态不一致,导致运行错误。线程安全要保证共享变量在执行过程中的读写有序,串行执行,保证同一时刻所有线程看到的变量状态一致。

      保证线程安全的方法有很多,最常见的有三种,synchronized同步方法或者同步块,ReentrantLock显示加锁和读写锁,使用线程安全的原子对象操作变量的读写,例如AtomicInteger等。同步相关的操作这个不做介绍,参考其他文章。

二、线程协同工作

      在编程中会遇到多个线程同时完成一个事情,在这个过程中需要线程之间协同工作,例如等待其他线程完成,触发其他线程执行等。协同工作有CountDownLatch,CyclicBarrier等常用功能。

1.CountDownLatch

      适合场景是若干个线程全部执行完成后,再执行其他任务。

public static void main(String[] args) {

CountDownLatch latch = new CountDownLatch(4);

 

new TestThread(latch).start();

 

new TestThread(latch).start();

new TestThread(latch).start();

 

new TestThread(latch).start();

 

try {

latch.await();

System.out.println("执行完成");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

 

public static class TestThread extends Thread{

 

CountDownLatch latch;

 

public TestThread(CountDownLatch latch){

this.latch = latch;

 

}

@Override

public void run() {

try {

Thread.sleep(1000);

latch.countDown();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

2.CyclicBarrier

      适合场景是多个线程执行到同一位置后,同时执行,如果到达这个位置的线程数量不够则线程等待。

public static void main(String[] args) {

CyclicBarrier barrier = new CyclicBarrier(3);

 

new ThreadTest(barrier,1000).start();

new ThreadTest(barrier,2000).start();

new ThreadTest(barrier,3000).start();

 

System.out.println("主线程执行完成");

}

 

 

public static class ThreadTest extends Thread{

 

private CyclicBarrier cb;

 

private int waittime;

 

public ThreadTest(CyclicBarrier cb,int waittime){

this.cb = cb;

this.waittime = waittime;

}

 

public void run(){

try {

Thread.sleep(waittime);

cb.await();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (BrokenBarrierException e) {

e.printStackTrace();

}

System.out.println("线程结束");

}

}

三、线程的停止、暂停、继续

      Java线程在设计之初没有考虑周全,Thread提供的stop,suspend,resume,destroy等方法都是线程不安全的,现在已经不推荐使用了。

1.stop方法会直接抛出ThreadDeath异常,导致线程终止,还有释放线程中持有的所有锁,如果线程对于共享变量进行了修改,使其处于不一致状态,其他线程再去访问这个对象就会产生意想不到的结果,甚至导致程序崩溃。

2.suspend,resume方法会导致线程的死锁,它会挂起线程持有的锁,如果一直这样可能导致其他需要该锁的线程死锁。

3.destroy方法压根就没有实现。

4.interrupt方法用于在线程没有响应或者处于阻塞状态时停止线程用的,它会抛出InterruptedException。在一些情况下可能不会响应interrupt方法,可以关闭相关资源方法促使线程结束,例如关闭Socket以结束线程。

5.线程停止的正确方法是定义一个volatile变量来标示线程是否停止,在线程执行中轮训这个变量来停止这个线程。例如下面这个例子。

 

public class ThreadStopEx extends Thread{

//标示线程是否停止标志

private volatile boolean  ISSTART = true;

 

@Override

public void run() {

 

while(ISSTART){

synchronized (this) {

try {

Thread.sleep(1000);

System.out.println("线程执行");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

 

}

 

public void stopThread() {

ISSTART = false;

this.interrupt();

}

 

public static void main(String[] args) throws InterruptedException {

ThreadStopEx thread = new ThreadStopEx();

thread.start();

Thread.sleep(2500);

thread.stopThread();

}

 

}

6.线程的暂停和继续解决方法。线程的暂停和继续使用suspend/resume方法会有产生线程死锁问题,建议使用wait和notify来进行线程的暂停和继续。下面是例子。

public class ThreadSuspendResumeEx {

 

private static Object waitObject = new Object();

 

 

public static void main(String[] args) throws InterruptedException {

new ThreadTest(1).start();

new ThreadTest(2).start();

new ThreadTest(3).start();

//等待1000毫秒

Thread.sleep(1000);

synchronized (waitObject) {

waitObject.notify();

}

System.out.println("主线程1000毫秒后唤醒一个线程。");

//等待2000毫秒

Thread.sleep(2000);

System.out.println("主线程3000毫秒后唤醒全部线程。");

synchronized(waitObject){

waitObject.notifyAll();

}

 

}

 

 

 

public static class ThreadTest extends Thread{

//线程编号

private int index;

 

public ThreadTest(int index){

this.index = index;

}

@Override

public void run() {

synchronized(waitObject){

try {

waitObject.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(index + "线程执行");

}

}

}

源码见附件。

 

分享到:
评论
1 楼 天天向上666 2017-07-10  

相关推荐

Global site tag (gtag.js) - Google Analytics