java并发编程——线程异常处理\资源共享的问题\ThreadLocal
2016-02-18 01:07
330 查看
Thread
到目前为之,我们应该清楚Thread只是你用来驱动任务(Runnable、Callable)的一个线程,我们对Thread并没有任何控制权,尤其是使用ExecutorService。Thread本身并没有任何逻辑,我们一般说的多线程,其实是指依附于多线程上的各种任务。要注意,将任务与线程分离是很有意义的实现,因为线程的创建开销是昂贵的,我们需要妥善管理并使用。线程中的异常处理
一个线程中的异常如果不在该线程中捕获(run方法),在其他的线程中是无法捕获的,最终抛到前台。package com.concurrent.newStart.exception; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; class ExceptionThread1 implements Runnable { @Override public void run() { Thread t = Thread.currentThread(); System.out.println("run() by " + t); // throw new RuntimeException(); int exception = 1 / 0; } } class ExceptionThread2 implements Runnable { @Override public void run() { Thread t = Thread.currentThread(); System.out.println("run() by " + t); System.out.println("the UncaughtExceptionHandler" + t.getUncaughtExceptionHandler()); // throw new RuntimeException(); int exception = 1 / 0; System.out.println("after throw exception .............. ");// never // gets here } } class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("caught " + e + " e.Message: " + e.getMessage()); } } class HandlerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { System.out.println(this + " creating new Thread by ThreadFactory"); Thread t = new Thread(r); System.out.println("created thread wiht UncaughtExceptionHandler " + t); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); System.out.println("the UncaughtExceptionHandler " + t.getUncaughtExceptionHandler()); return t; } } public class CaptureUncaughtException { private static void testnWithUncaughtExceptionHandler(Runnable task) { ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory()); exec.execute(task); exec.shutdown(); System.out.println(task.getClass() + " --end"); } private static void testnWithOutUncaughtExceptionHandler(Runnable task) { ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(task); exec.shutdown(); System.out.println(task.getClass() + " --end"); } public static void main(String[] args) throws Exception { // 如果所有线程的异常处理器一样,可以为所有线程设置默认的 UncaughtExceptionHandler. // 默认的UncaughtExceptionHandler,仅再没有专有的UncaughtExceptionHandler时最后调用 . // Thread.setDefaultUncaughtExceptionHandler(new // MyUncaughtExceptionHandler()); testnWithUncaughtExceptionHandler(new ExceptionThread2()); System.out.println("------------------------------------------------"); TimeUnit.SECONDS.sleep(1); try { testnWithOutUncaughtExceptionHandler(new ExceptionThread1()); } catch (Exception e) { System.out.println("can i catch exception of another thread? e:" + e);// never } } }
结论:一个线程中的异常如果发生逃逸,逃逸的异常无法再其他线程中捕获,会一 直抛出到控制台;
当我们使用UncaughtExceptionHandler 可以防止异常逃逸的发生.如果不使用,那务必在run方法中try可能出现的异常
资源共享的问题与解决
package com.tij.concurrency.shareResource; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; abstract class IntGenerator { private volatile boolean canceled = false; public abstract int next(); // Allow this to be canceled: public void cancel() { canceled = true; } public boolean isCanceled() { return canceled; } } public class EvenGenerator extends IntGenerator { private int currentEvenValue = 0; public int next() { ++currentEvenValue; // Danger point here! Thread.yield(); //加快线程切换 ++currentEvenValue; return currentEvenValue; } public static void main(String[] args) { EvenGenerator sharedResource = new EvenGenerator(); EvenChecker.test(sharedResource);// } } class EvenChecker implements Runnable { private IntGenerator generator; private final int id; public EvenChecker(IntGenerator g, int ident) { generator = g; id = ident; } public void run() { while (!generator.isCanceled()) { int val = generator.next(); System.out.println("val:" + val); 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++) { System.out.println("count" + i); exec.execute(new EvenChecker(gp, i)); } exec.shutdown(); } // Default value for count: public static void test(IntGenerator gp) { test(gp, 2); } } // outPut: // Press Control-C to exit // count0 // count1 // val:3 // 3 not even! // val:4
代码中 我们使用了 Thread.yield(); 去加快线程切换。但是 yield()、setPriority()只是给线程调度调度器 建议,未必会有作用,因此我们实际使用中不能依赖它们。
我们定义了一个 EvenGenerator sharedResource = new EvenGenerator();这个对象作为共享资源,在多个线程中进行叠加。我们模拟的场景中:当 线程1 val加1,这时候发生上下文切换(不可控),线程2运行, 线程2中 val加1 (val==2)后立刻切换线程1,然后刚才挂起的线程1继续running,加1, 执行判断,此时val==3,出现奇数,任务执行完毕。
上述的问题只是一个典型的特例,线程间的切换不可控,导致资源的读写执行情况与预期不符合!
就像小明和小花 并行使用桌上唯一的筷子,小明拿起筷子使用后放下,然后小花使用。ok没问题,这是单线程,小明小花排队拿筷子。当小明拿起筷子后还未返还,小花又要拿筷子,这时候小花当然拿不到,小花会认为桌上就没有筷子。这就是多线程中共享资源出现的问题!小明小花不会排队拿筷子
怎么解决呢?
都是采用 串行访问共享资源的方式解决,通过加锁的方式实现共享资源的多线程访问互斥性。这种机制成为 互斥量 mutex.
接着上文的例子,也就是说当小明拿起筷子还没有放回桌子上时,”不允许”小花伸手拿筷子(让小花等待)。当小明换了筷子后,”允许”小花伸手去取筷子。
线程本地存储
防止在共享资源上产生冲突的第二种方式是根除对变量的共享:线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储。
JDK实现:java.lang.ThreadLocal
//验证分线程存储变量 Test import java.util.concurrent.TimeUnit; /** * * @author zs */ public class TestThreadLocal { ThreadLocal<String> threadLocal = new ThreadLocal<String>(); public static void main(String[] args) { final TestThreadLocal instance1 = new TestThreadLocal(); // 线程本地存储: Thread t1 = new Thread(new Runnable() { public void run() { while (!Thread.interrupted()) { if (null == instance1.threadLocal.get()) { String temp = "" + Thread.currentThread(); instance1.threadLocal.set(temp); } System.out.println( "--------thread 1 show threadLocal message: -------" + instance1.threadLocal.get()); sleepThreeSecond(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { public void run() { while (!Thread.interrupted()) { if (null == instance1.threadLocal.get()) { String temp = "" + Thread.currentThread(); instance1.threadLocal.set(temp); } System.out.println( "--------thread 2 show threadLocal message: -------" + instance1.threadLocal.get()); sleepThreeSecond(); } } }, "t2"); t1.start(); t2.start(); while (true) { System.out .println("--------other thread show threadLocal message: -------" + instance1.threadLocal.get()); sleepThreeSecond(); } } public static void sleepThreeSecond() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } }
class Accessor implements Runnable { private final int id; public Accessor(int id) { this.id = id; } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { ThreadLocalVariableHolder.increment(); System.out.println(this); Thread.yield(); } } @Override public String toString() { return "Accessor [id=" + ThreadLocalVariableHolder.get() + "]"; } } public class ThreadLocalVariableHolder { // 1.ThreadLocal 通常当作静态域使用 // 2.对ThreadLocal对象,只能通过get\set方法访问其内容 // 3.get方法将返回该线程相关联的对象副本,set方法类似 private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() { // private Random rand = new Random(47); protected synchronized Integer initialValue() { // return rand.nextInt(10000); return 2; } }; // 4.访问方法 increment、get都不是synchronized,因为ThreadLocal保证不会出现竞争情况 public static void increment() { value.set(value.get() + 2); } 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(); } }
相关文章推荐
- sublime 编译java
- Java内部类
- java.lang.NullPointerException: Expected timestamp
- Mybatis Could not find result map java.lang.Intege
- java 通过Spark实现单词计数的功能
- SpringMVC 使用Fastjson代替Jackson
- 在web应用中获取spring的context,并打印出context中所有bean的id
- Spring+SpringMVC+MyBatis框架搭建
- jdk_自带的 logUtils日志记录工具
- java md5加密 简单加密
- java io 字符流操作工具类
- java递归算法+遍历磁盘文件
- spring单元测试
- spring事务----编程式与声明式
- eclipse快捷键
- Java内部类【转】
- Java RMI详解[转]
- java 对象排序
- Java多线程参考资料
- Spring配置多个properties的方式