Thinking in java之并发

21.2.14 捕获异常

在主线程中是不能直接捕获子线程的异常的

package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExceptionThread implements Runnable{

    @Override
    public void run() {
        throw new RuntimeException();
    }
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }

}

将main的主体放到try-catch语句块中是没有作用的。

package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NativeExceptionHanding {
    public static void main(String[] args) {
        try {
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(new ExceptionThread());
        } catch (RuntimeException e) {
            // This statement will NOT execute!
            System.out.println("Exception has been handled!");
        }

    }
}

解决之道:

在Thread类中有如下属性:

private volatile UncaughtExceptionHandler uncaughtExceptionHandler;

这是一个接口,只有一个方法:

    public interface UncaughtExceptionHandler {
        void uncaughtException(Thread t, Throwable e);
    }

这个方法会在线程因未捕获的异常而临近死亡时被调用。

package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

class ExceptionThread2 implements Runnable {
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run() by " + t);
        System.out.println("en = " + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught " + e);

    }
}

class HandlerTreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {//传递进来的新创建的线程
        System.out.println(this + " creating new Thread");
        Thread t = new Thread(r);
//        System.out.println("created " + t);
        //设置线程uncaughtExceptionHandler,这个接口中的方法会在线程因未捕获的异常而临近死亡时被调用
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
//        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        return t;
    }
}

public class CaptureUncaughtException {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool(new HandlerTreadFactory());
        exec.execute(new ExceptionThread2());
    }
}

上面重写了ThreadFactory方法,会给新创建的线程设置UncaughtExceptionHandler属性

当在代码中处处使用相同的异常处理器,可以在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常处理器。

package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SettingDefaultHandler {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }
}

这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。系统会检查线程专有版本,如果没有发现,则检查线程组是否有其专有的uncaughtException()方法,如果也没有,再调用defaultUncaughtExceptionHandler。

21.3 共享受限资源

21.3.1 不正确地访问资源

 

package concurrency;

public abstract class IntGenerator {
    // boolean类型是原子型,即诸如赋值和返回值这样的简单操作在发生时没有中断的可能
    private volatile boolean canceled = false;

    public abstract int next();

    // Allow this to be canceled:
    public void cancel() {
        canceled = true;
    }

    public boolean isCanceled() {
        return canceled;
    }
}

 

package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EvenChecker implements Runnable {
    private IntGenerator generator;
    private final int id;

    public EvenChecker(IntGenerator generator, int id) {
        this.generator = generator;
        this.id = id;
    }

    @Override
    public void run() {
        while (!generator.isCanceled()) {
            int val = generator.next();
            if (val % 2 != 0) {
                System.out.println(val + " not even!");
                generator.cancel();// Cancels all EvenCheckers
            }
        }
    }

    // Test any type of IntGenerator
    public static void test(IntGenerator gp, int count) {
        System.out.println("Press Control-C to exit");
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < count; i++) {
            exec.execute(new EvenChecker(gp, i));
        }
        exec.shutdown();
    }

    // Default value for count:
    public static void test(IntGenerator gp) {
        test(gp, 10);
    }
}

 

 

package concurrency;

public class EvenGenerator extends IntGenerator{
    private int currentEvenValue = 0;
    
    @Override
    public int next() {
        //递增程序自身也需要多个步骤,并且在递增过程中可能会被线程机制挂起——在Java中,递增不是原子性的操作
        //因此,如果不保护任务,即使单一的递增也不是安全的。
        ++currentEvenValue; //Danger point here!
        ++currentEvenValue;
        return currentEvenValue;
    }
    public static void main(String[] args) {
        EvenChecker.test(new EvenGenerator());
    }

}

21.3.2 解决共享资源竞争

所有对象都自动含有单一的锁(也称为监视器)。当在对象上调用其任意synchronized方法的时候,此对象都被加锁,这时该对象上的其他synchronized方法只有等到前一个方法调用完毕并释放了锁之后才能被调用。对于某个特定对象来说,其所有synchronized方法共享一个锁。

针对每个类,也有一个锁(作为类的Class对象的一部分),所以synchronized static方法可以在类的范围内防止对static数据的并发访问。

每个访问临界共享资源的方法都必须被同步,否则它们就不会正确地工作。

 同步控制EvenGenerator

package concurrency;

public class SynchronizedEvenGenerator extends IntGenerator{
    private int currentEvenValue = 0;
    @Override
    public synchronized int next() {
        ++currentEvenValue;
        Thread.yield(); //Cause failure faster
        ++currentEvenValue;
        return currentEvenValue;
    }
    public static void main(String[] args) {
        EvenChecker.test(new SynchronizedEvenGenerator());
    }
}

使用显示的Lock对象

posted on 2013-04-21 10:36  只愿软禁  阅读(247)  评论(0)    收藏  举报