前话
最近项目中因为需要用到多线程处理数据,在Java中,我们通常使用两种方式来创建线程:集成Thread类和实现Runnable接口。Java还提供了一个接口,既ThreadFactory接口,用于创建你自己的线程对象工厂,可以设置线程名称、优先级等属性。
ThreadFactory
为什么要用ThreadFactory来创建线程呢?
- 自定义具有描述意义的线程名称。如果使用默认的ThreadFactory,它给线程起名字大概规律就是pool-m-thread-n,但是当你分析一个thread dump时,看着这样的名字就很难知道线程的目的。所以使用一个有描述意义的线程名称是分析追踪问题的关键。
- 设置线程是否是守护线程,默认的ThreadFactory总是提交非守护线程。
- 设置线程优先级,默认ThreadFactory总是提交的一般优先级线程。
实例:
1、首先我们定义一个自己的线程任务,通过实现Callable(Callable相比Runnable而言可以得到线程的返回值)
/**
* 线程任务只返回当前线程的名称
*/
public class TestThreadTask implements Callable<String> {
@Override
public String call() {
return Thread.currentThread().getName();
}
}
2、定义自己的TreadFactory,只需要实现ThreadFactory接口,并重写其中的newTread方法。
public class MyThreadFactory implements ThreadFactory {
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
/**
*
*/
public MyThreadFactory() {
SecurityManager securityManager = System.getSecurityManager();
group = (securityManager != null) ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "chengh-test-thread-";
}
/**
*
* @param runnable
* @return
*/
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(group, runnable, namePrefix + threadNumber.getAndIncrement(), 0);
if (thread.isDaemon()) {
thread.setDaemon(false);
}
if (thread.getPriority() != Thread.NORM_PRIORITY) {
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
}
3、在spring容器中创建自己的ThreadPool
@Configuration
public class MyThreadPoolConfig {
/**
* 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务, 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
* 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
*/
private static final int corePoolSize = 10; // 核心线程数(默认线程数)
private static final int maxPoolSize = 60; // 最大线程数
private static final int keepAliveTime = 5; // 允许线程空闲时间(单位:默认为秒)
/**
*
* @return
*/
@Bean("threadPoolExecutor") // bean的名称,默认为首字母小写的方法名
public ThreadPoolExecutor threadPoolExecutor() {
return new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS,
new LinkedBlockingDeque(), new MyThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
}
}
4、使用,我这里是写在junit里的,只需要注入我们自己的threadPoolExecutor就ok了
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = TestAplication.class)
@WebAppConfiguration
public class UserTest {
@Resource
private ThreadPoolExecutor threadPoolExecutor;
@Test
public void threadTest() {
List<Future<String>> testThreadTasks = Lists.newArrayList();
for (int i = 0; i < 100; i++) {
TestThreadTask task = new TestThreadTask();
testThreadTasks.add(threadPoolExecutor.submit(task));
}
testThreadTasks.forEach(testThreadTask -> {
try {
System.out.println(testThreadTask.get()) ;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
;
});
}
}
打印出来的结果:
chengh-test-thread-1
chengh-test-thread-2
chengh-test-thread-3
chengh-test-thread-4
chengh-test-thread-5
chengh-test-thread-6
chengh-test-thread-7
chengh-test-thread-8
chengh-test-thread-9
chengh-test-thread-10
chengh-test-thread-3
chengh-test-thread-4
chengh-test-thread-2
chengh-test-thread-6
........
总结:
其实使用起来挺简单,只需要创建一个我们自己的线程任务、TreadFactory,再构造自定义的TreadPool,提交任务执行就可以了。