java线程实践与总结

java线程实践与总结

学习教程

线程状态

状态类型在枚举类java.lang.thread.State中定义

  • NEW-新建:线程刚创建,还没有启动
  • RUNNABLE-就绪:可运行(已进入cpu就绪队列,等待分配时间片)或正在运行
  • BLOCKED-阻塞:等待其他线程释放锁
  • WAITING-等待:进入条件等待状态,调用线程的wait方法进入等待状态,调用notify方法线程继续工作,典型应用:生产者与消费者线程
  • TIMED_WAITING-计时等待:与WAITING类似,TIMED_WAITING有时间限制,即:在调用wait方法时,传入一个时间
  • TERMINATED-终止:线程正常退出或者异常退出,不可再次启动

给创建的线程设置名称

当出现线上故障时,如果线程没有设置名称很多时候很难定位到是哪个线程出现问题。

Thread thread = new Thread("thread-http");

如何停止线程

不要使用thread的stop和destory方法来中断线程,可能会出现不可预料的结果。正确的做法是使用标记状态字段来控制。

示例代码中running必须使用volatile来修饰

/**
 * 停止线程示例
 *
 * @author fengjianxin
 */
public class ThreadStop extends Thread {

    private volatile boolean running = true;

    public static void main(String[] args) {
        ThreadStop thread = new ThreadStop();
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.setRunning(false);
        // 不要使用stop停止线程
        // thread.stop();
    }

    @Override
    public void run() {
        while (running) {
        }
        System.out.println("stop...");
    }

    public boolean isIsRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }
}

要响应线程中断异常

当线程抛出InterruptedException时,表示当前线程有阻塞操作,并且该线程任务被意外终止,同时终止原因难以预测,完善的程序应该对这种情况进行处理。

教程:https://www.ibm.com/developerworks/cn/java/j-jtp05236.html

ThreadLocal使用

ThreadLocal通常定义成静态变量,用于共享当前线程数据,位置线程上下文数据,隐式传参。在线程结束时必须调用remove方法,否则可能会导致内存泄漏。

例如:spring aop事务管理,将数据库connection放到ThreadLocal,来保证打开和关闭事务的connection是同一个,而无需显示调用和将connection显式传递。

谨慎使用Executors创建线程池

不要直接使用Executors创建线程池,因为Executors默认创建线程池的队列大小为Integer.MAX_VALUE,大量并发可能会导致内存泄漏。正确的做法是使用ThreadPoolExecutor来创建。

以下摘自阿里巴巴java开发手册

Executors各个方法的弊端:

  1. newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
  2. newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

Positive example 1:

//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

Positive example 2:

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown

Positive example 3:

<bean id="userThreadPool"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="10" />
    <property name="maxPoolSize" value="100" />
    <property name="queueCapacity" value="2000" />

<property name="threadFactory" value= threadFactory />
    <property name="rejectedExecutionHandler">
        <ref local="rejectedExecutionHandler" />
    </property>
</bean>
//in code
userThreadPool.execute(thread);