多线程使用注意

我们在创建线程池的时候,一定要给线程池名字,如下这种写法,线程是默认直接生成的:

public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int finalI = i; executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":" + finalI); } }); } }

最后的输出:

pool-1-thread-3:2 pool-1-thread-2:1 pool-1-thread-3:4 pool-1-thread-1:3 pool-1-thread-3:6 pool-1-thread-2:5 pool-1-thread-3:8 pool-1-thread-1:7 pool-1-thread-2:9

Executors中有默认的线程工厂的实现:

static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }

我们可以改造一下

public class NamedThreadFactory implements ThreadFactory { private final AtomicInteger threadNumber; private final String name; private final boolean isDaemon; public NamedThreadFactory(String name) { this(name, false); } public NamedThreadFactory(String name, boolean daemon) { this.threadNumber = new AtomicInteger(1); this.isDaemon = daemon; this.name = name + "-thread-pool-"; } public Thread newThread(Runnable r) { Thread t = new Thread(r, this.name + this.threadNumber.getAndIncrement()); t.setDaemon(this.isDaemon); if (t.getPriority() != Thread.NORM_PRIORITY){ t.setPriority(Thread.NORM_PRIORITY); } return t; } }

那我们看下改造之后的输出结果:

有名字的线程池-thread-pool-1:0 有名字的线程池-thread-pool-3:2 有名字的线程池-thread-pool-1:3 有名字的线程池-thread-pool-2:1 有名字的线程池-thread-pool-1:5 有名字的线程池-thread-pool-1:7 有名字的线程池-thread-pool-1:8 有名字的线程池-thread-pool-3:4 有名字的线程池-thread-pool-1:9 有名字的线程池-thread-pool-2:6

这样的话,当我们应用线上出现问题,需要通过jstack查看线程堆栈的时候,就可以知道是哪些线程出现的问题,否则看到的都是统一的命名方式,看到都是清一色的线程,增加排查问题的难度

Thread异常处理

Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常,

public interface Runnable { public abstract void run(); }

那么如果 run() 方法中抛出了RuntimeException,将会怎么处理了?

线程出现未捕获异常后,JVM将调用Thread中的dispatchUncaughtException方法把异常传递给线程的未捕获异常处理器

private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); } public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; }

Thread中存在两个UncaughtExceptionHandler。一个是静态的defaultUncaughtExceptionHandler,另一个是非静态uncaughtExceptionHandler。

// null unless explicitly set private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

defaultUncaughtExceptionHandler:设置一个静态的默认的UncaughtExceptionHandler。来自所有线程中的Exception在抛出并且未捕获的情况下,都会从此路过。进程fork的时候设置的就是这个静态的defaultUncaughtExceptionHandler,管辖范围为整个进程

uncaughtExceptionHandler:为单个线程设置一个属于线程自己的uncaughtExceptionHandler,辖范围比较小。

如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。ThreadGroup类定义:

private ThreadGroup group; class ThreadGroup implements Thread.UncaughtExceptionHandler{}

ThreadGroup实现的uncaughtException如下:

public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } }

默认情况下,线程组处理未捕获异常的逻辑是,首先将异常消息通知给父线程组,然后尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常,如果没有默认的异常处理器则将错误信息输出到System.err。也就是JVM提供给我们设置每个线程的具体的未捕获异常处理器,也提供了设置默认异常处理器的方法,通常java.lang.Thread对象运行设置一个默认的异常处理方法:

public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission( new RuntimePermission("setDefaultUncaughtExceptionHandler") ); } defaultUncaughtExceptionHandler = eh; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxwjx.html