创建线程池的正确姿势,请给它指定一个有意义的名字

为什么我们创建线程或者线程池的时候,需要指定有意义的线程名称?

最终目的是为了方便回溯。

我们在日常开发中,一个项目中会创建很多个线程池用来资源隔离,但是如果我们没有一个好的命名的话,出问题的时候就会难以定位。

public class Demo5 {
​
    public static void main( String[] args ) throws IOException {
        new Thread(()->{
            System.out.println("保存用户信息..........");
​
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            throw new NullPointerException();
        }).start();
        System.in.read();
    }
}

上面代码是启动一个线程来保存用户信息,然后抛出异常!

报错信息如下:

file

从运行错误可以分析,Thread-0 抛出了空指针,那么单从这个日志根本无法判断用户模块线程抛出的异常。我们先分析一下Thread-0是怎么来的。

我们先看下创建线程的代码:

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
  private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

从上面代码可知,如果我们没有指定线程名称,内部会自动为我们创建线程名称"Thread-"+nextThreadNum()作为线程的默认名。

如果一个系统中有多个业务模块,如用户模块、订单模块、购物车模块等都使用自己的线程池,而没有指定名称,抛出的异常除非与业务内容有关,否则,根本无法判断是哪一个模块出了问题。

创建线程池时候也需要指定线程池的名称

我们可以使用threadFactory来指定线程的名称:

(1) 可以通过线程工厂给每个创建出来的线程设置更有意义的名字。线程池的命名时通过给这个factory增加组前缀来实现的。在虚拟机栈分析时,就可以知道线程任务由哪个线程工厂产生的。

(2) 使用Guava设置线程名字

new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();

(3)自定义实现:ThreadFactory

Executors静态工厂里默认的threadFactory,线程的命名规则是“pool-数字-thread-数字”。

我们看下线程池:

public class Demo6 {
​
    public static void main( String[] args ) throws IOException {
        ThreadPoolExecutor executorOne = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
​
        executorOne.execute(()->{
            System.out.println("保存用户信息");
            throw new NullPointerException();
        });
        System.in.read();
    }
}

结果:

file

我们看到的打印线程池名称是 pool-1-thread-1,那么怎么来的呢?

那我们看下ThreadPoolExecutor源码

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
 public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }
DefaultThreadFactory() {
      SecurityManager s = System.getSecurityManager();
      group = (s != null) ? s.getThreadGroup() :
                            Thread.currentThread().getThreadGroup();
      namePrefix = "pool-" +
                    poolNumber.getAndIncrement() +
                   "-thread-";
  }
    ```
    ```
 private static final AtomicInteger poolNumber = new AtomicInteger(1);

那我们怎样定义ThreadFactory呢?

public class NamedThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private  String namePrefix;
    private final ThreadGroup group;
​
    public NamedThreadFactory( String name ) {
        this.namePrefix = namePrefix = name + "-" + poolNumber.getAndIncrement() + "-thread-";
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
    }
​
    @Override
    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 Demo6 {
​
    public static void main( String[] args ) throws IOException {
        ThreadPoolExecutor executorOne = new ThreadPoolExecutor(5, 5, 1,
                TimeUnit.MINUTES, new LinkedBlockingQueue<>(),new NamedThreadFactory("UserModule"));
​
        executorOne.execute(()->{
            System.out.println("保存用户信息");
            throw new NullPointerException();
        });
        System.in.read();
    }
}

结果:

file

我们看到,从错误栈中的线程名称,我们就可以定位到是哪个模块出现了错误。

总结

创建线程或线程池时请 指定有意义的线程名称,方便出错时回溯。

自定义线程工厂,并且根据外部特征进行分组,比如,来自同一机房的调用,把机房编号赋值给

file

文源网络,仅供学习之用,如有侵权,联系删除。

我将面试题和答案都整理成了PDF文档,还有一套学习资料,涵盖Java虚拟机、spring框架、Java线程、数据结构、设计模式等等,但不仅限于此。

关注公众号【java圈子】获取资料,还有优质文章每日送达。

file

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用PyQt线程池的简单示例代码: ```python from PyQt5.QtCore import QRunnable, QThreadPool, Qt from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel class Worker(QRunnable): def __init__(self, data): super().__init__() self.data = data def run(self): # 模拟耗时操作 result = self.data * 2 # 发送结果信号 self.result_signal.emit(result) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Thread Pool Example") self.central_widget = QWidget(self) self.setCentralWidget(self.central_widget) self.layout = QVBoxLayout(self.central_widget) self.label = QLabel("Data: 0", self.central_widget) self.layout.addWidget(self.label) self.button = QPushButton("Start", self.central_widget) self.layout.addWidget(self.button) self.button.clicked.connect(self.start_worker) self.threadpool = QThreadPool() def start_worker(self): data = 10 worker = Worker(data) worker.result_signal.connect(self.update_data) # 将任务添加到线程池中 self.threadpool.start(worker) def update_data(self, result): self.label.setText(f"Data: {result}") if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() app.exec() ``` 在这个示例中,我们创建一个主窗口,并包含一个标签 `QLabel` 和一个按钮 `QPushButton`。当点击按钮时,会创建一个Worker对象,该对象会在后台线程中执行耗时操作,并将结果通过信号 `result_signal` 发送回主线程。主线程接收到结果后,调用 `update_data` 方法更新标签的内容。 我们使用了PyQt提供的线程池 `QThreadPool` 来管理多个线程对象。通过将任务添加到线程池中,线程池会自动调度线程并执行任务。 这个示例展示了如何使用PyQt线程池来执行后台任务,并在主线程中更新UI界面。你可以根据自己的需求修改代码,例如添加更多的任务、处理更复杂的逻辑等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值