Thinking in Java 之 多线程 2

来源:互联网 发布:java代理模式的好处 编辑:程序博客网 时间:2024/06/15 09:24

Thinking in Java 之 多线程

Catching Exception

class ExceptionThread2 implements Runnable{    @Override    public void run() {        Thread t = Thread.currentThread();        System.out.println("run() by " + t);        System.out.println("eh = " + t.getUncaughtExceptionHandler());        throw new RuntimeException();    }}class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{    @Override    public void uncaughtException(Thread t, Throwable e) {        System.out.println("caught " + e);    }}class HandlerThreadFactory implements ThreadFactory{    @Override    public Thread newThread(Runnable r) {        System.out.println(this + "creating new Thread");        Thread t = new Thread(r);        System.out.println("create " + t);        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());        System.out.println("eh = " + t.getUncaughtExceptionHandler());        return t;    }}public class CaptureUncaughtException {    public static void main(String[] args) {        ExecutorService exec = Executors.newSingleThreadExecutor(new HandlerThreadFactory());        exec.execute(new ExceptionThread2());        exec.shutdown();//如果不加将会是第二种结果    }}

结果:
这里写图片描述

结果2:会自动多创建一条线程,换成CachedThreadPool也一样。
这里写图片描述
————————————
默认异常处理机制:

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

这里eh和上面不同。
这里写图片描述

Using explicit Lock object

Right after the call to lock(), you must place a try-finally statement with unlock() in the finally clause – this is the only way to guarantee that the lock is always released.
Note that the return statement must occur inside the try clause to ensure that unlock() doesn’t happen too early and expose the data to a second task.

Atomicity and volatility

This is true even id local caches are involved–volatile fields are immediately written through to main memory, and reads occur from main memory.

If multiple tasks are accessing a field, that field should be volatile; otherwise, the field should only be accessed via synchronization. Synchronization also causes flushing to main memory, so if a field is completely guarded by synchronized methods or blocks, it is not necessary to make it volatile.

If you define a variable as volatile, it tells the compiler not to do any optimizations that would remove reads and writes that keep the field in exact synchronized with the local data in the threads. In effect, reads and writes go directly to memory, and not cached. volatile also restricts compiler reordering of accessed during optimization. However, volatile doesn’t affect the fact that an increment isn’t an atomic operation.

synchronized

The synchronized keyword is not part of the method signature and thus may be added during overriding.

This is a typically the reason to use a synchronized block instead of synchronizing the whole method: to allow other tasks more access(as long as it is safe to do so).

Thread local storage

class Accessor implements Runnable {//  private final int id;    public Accessor(int idn) {//      id = idn;    }    @Override    public void run() {        while(!Thread.currentThread().isInterrupted()) {            ThreadLocalVariableHolder.increment();            System.out.println(this);            Thread.yield();        }    }    @Override    public String toString() {//      return "#" + id + ": " + ThreadLocalVariableHolder.get();        return Thread.currentThread().getName() + "--->" + ThreadLocalVariableHolder.get();    }}public class ThreadLocalVariableHolder {    //value其实是一个LocalThreadMap,以currentThread为键    private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() {        private Random rand = new Random(47);//      protected T initialValue() {//重写(覆盖)这个方法//          return null;//      }        protected synchronized Integer initialValue() {            return rand.nextInt(10000);        }    };    public static void increment() {        value.set(value.get() + 1);    }    public static int get() {        return value.get();    }    public static void main(String[] args) throws InterruptedException {        ExecutorService exec = Executors.newCachedThreadPool();        for(int i=0;i<5;i++) {            exec.execute(new Accessor(i));        }        TimeUnit.SECONDS.sleep(1);          exec.shutdownNow();// 注意是不是shutDown()    }}

Notice that increment() and get() are not synchronized, because ThreadLocal guarantee that no race condition can occur.
If you call shutdownNow() on an Executor, it will send an interrupt() call to each of the threads it has started.

Terminating tasks

The ornamental garden

class Count {    private int count = 0;    private Random rand = new Random(47);    // Remove the synchronized keyword to see counting fail:    public synchronized int increment() {        int temp = count;        if (rand.nextBoolean()) {// Yield half the time            Thread.yield();        }        return (count = ++temp);    }    public synchronized int value() {        return count;    }}class Entrance implements Runnable {    private static Count count = new Count();    private static List<Entrance> entrances = new ArrayList<Entrance>();    private int number = 0;    //Doesn't need synchronizatoion to read:    private final int id;    private static volatile boolean canceled = false;    // Atomic operation on a volatile field:    // 在Java中,对基本数据类型变量的取值和赋值操作是原子性操作。    public static void cancel() {        canceled = true;    }    public Entrance(int id) {        this.id = id;        // keep this task in a list, Also prevents garbage collection of dead tasks:        entrances.add(this);    }    @Override    public void run() {        while(!canceled) {            synchronized(this) {                ++number;            }            System.out.println(this + " Total: " + count.increment());            try {                TimeUnit.MILLISECONDS.sleep(100);            } catch (InterruptedException e) {                System.out.println("sleep interrupted");            }        }        System.out.println("Stopping " + this);    }    public synchronized int getValue() {        return number;    }    public String toString() {        return "Entrance " + id + ": " + getValue();    }    public static int getTotalCount() {        return count.value();    }    public static int sumEntrances() {        int sum = 0;        for(Entrance entrance : entrances) {            sum += entrance.getValue();        }        return sum;    }}public class OrnametalGarden {    public static void main(String[] args) throws InterruptedException {        ExecutorService exec = Executors.newCachedThreadPool();        for(int i=0;i<5;i++) {            exec.execute(new Entrance(i));        }        // Run for a while, then stop and collect the data:        TimeUnit.SECONDS.sleep(3);        Entrance.cancel();        exec.shutdown();        if(!exec.awaitTermination(250, TimeUnit.MILLISECONDS)) {            System.out.println("Some tasks were not terminated!");        }        System.out.println("Total: " + Entrance.getTotalCount());        System.out.println("Sum of Entrances: " + Entrance.sumEntrances());    }}

increment()使用synchronized结果正常:
这里写图片描述
不使用synchronized结果不正确:
这里写图片描述
The yield causes the problem to happen more quickly. And yield() method never release the lock.

Terminating when blocked

class SleepBlocked implements Runnable {    @Override    public void run() {        try {            TimeUnit.SECONDS.sleep(100);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("Exiting SleepBlock.run()");    }}class IOBlocked implements Runnable {    private InputStream in;    public IOBlocked(InputStream in) {        this.in = in;    }    @Override    public void run() {        try {            System.out.println("Waiting for read():");            in.read();        } catch (IOException e) {            if (Thread.currentThread().isInterrupted()) {                System.out.println("Interrupted from blocked I/O");            } else {                throw new RuntimeException(e);            }        }        System.out.println("Exiting IOBlock.run()");    }}class SynchronizedBlocked implements Runnable{    public synchronized void f() {        while(true) {// Never releases lock            Thread.yield();        }    }    public SynchronizedBlocked() {        new Thread() {            public void run() {                f();// Lock acquired by this thread             }        }.start();    }    @Override    public void run() {        System.out.println("Trying to call f()");        f();        System.out.println("Exiting SynchroziedBlicked.run()");    }}public class Interrupting {    private static ExecutorService exec = Executors.newCachedThreadPool();    static void test(Runnable r) throws InterruptedException {        Future<?> f =  exec.submit(r);        TimeUnit.MILLISECONDS.sleep(100);        System.out.println("Interrpting " + r.getClass().getName());        f.cancel(true);//Interruptes if running         System.out.println("Interrupt sent to " + r.getClass().getName());    }    public static void main(String[] args) throws InterruptedException {        test(new SleepBlocked());        test(new IOBlocked(System.in));        test(new SynchronizedBlocked());        TimeUnit.SECONDS.sleep(3);        System.out.println("Aborting with System.exit(0)");        System.exit(0); // ... since last 2 interrupts failed     }}

这里写图片描述

cancel() is a way to interrupt individual threads started with an Executor.

SleepBlock is an example of interruptible blocking, whereas IOBlocked and SynchronizedBlocked are uniterruptible blocking.

Checking for an interrupt

The following example shows the typical idiom that you should use in your run() method to handle both blocked and non-blocked possibilities when the interrupted status is set:

class NeedsCleanup{    private final int id;    public NeedsCleanup(int ident) {        id = ident;        System.out.println("NeedsCleanup " + id);    }    public void cleanup() {        System.out.println("Cleaning up " + id);    }}class Blocked3 implements Runnable{    private volatile double d = 0.0;    @Override    public void run() {        try {            while(!Thread.interrupted()) {                // point1                NeedsCleanup n1 = new NeedsCleanup(1);                try {                    // Start try-finally immediately after definition                    // of n1, to guarantee proper cleanup of n1:                    System.out.println("Sleeping");                    TimeUnit.MILLISECONDS.sleep(1);                    // point2                    NeedsCleanup n2 = new NeedsCleanup(2);                    // Guarantee proper cleanup of n2:                    try {                        System.out.println("Calculating");                        for(int i=1;i< 2500000;i++) {                            d += (Math.PI + Math.E) / d;                        }                        System.out.println("Finished time-consuming operation");                    } finally {                        n2.cleanup();                    }                } finally {                    n1.cleanup();                }            }        } catch (InterruptedException e) {            System.out.println("Exting via InterruptedException");        }    }}public class InterruptingIdiom {    public static void main(String[] args) throws InterruptedException {        Thread t = new Thread(new Blocked3());        t.start();        TimeUnit.MILLISECONDS.sleep(50);        t.interrupt();    }}