Java - 线程和线程池
Java - 线程
通过本篇博客简单记录下自己的学习,并希望以后养成写博客的习惯
在Java中线程四种实现方式
Java 中线程的四种实现方式分别是 继承Thread类,实现Runnable接口,实现Callable接口,和线程池。
1.继承Thread类
/**
* 1.Thread类
* 使用Thread类实现线程时,每创建一个线程就会创建一个具体的对象
*
*/
public static void main(String[] args) {
//1.Thread类
Thread thread = new ExtendThread();
thread.start();
}
/**
* 继承Thread类并且重写run方法
*
*/
class ExtendThread extends Thread{
@Override
public void run() {
System.out.println("ExtendThread running...");
}
}
2.实现Runnable接口
/**
* 2.Runnable接口
* 使用Runnable接口实现线程时,不管创建多少个线程都只用创建一个实现Runnable接口的具体对象
* 但每创建一个线程其实也会创建一个Thread对象
*
*/
public static void main(String[] args) {
//2.Runnable接口
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Runnable interface thread running....");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
3.实现Callable接口
/**
* 3.Callable接口
* 使用Callable接口实现线程的优势是,可以给线程一个返回参数并获取。
*
* @throws ExecutionException
* @throws InterruptedException
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
//3.Callable接口
Callable<Integer> callable = new Callable() {
@Override
public Object call() throws Exception {
System.out.println("Callable interface thread running....");
return 1;
}
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
//通过FutureTask类的get方法而已获取线程的返回值
//调用get方法,会阻塞等待对应线程执行完成并返回值
System.out.println("get Callable Thread return value: "+futureTask.get());
}
一开始我其实不明白第一种方法和第二种方法的区别,在使用一段时间后其实明白了,在实际运用中我们一般会优先于使用实现Runnable接口的方法,因为这种方法有如下优点:
- 相较于继承类的方法来实现线程,实现接口会更能避免局限性,因为在实际运用中你的基类可能另有其人
- 上面我们说过继承Thread类这种方法,在创建线程时会创建多个ExtendThread对象,而实现Runnable这种方法不管创建多少个线程都只会创建一个ImplementRunnable的对象,这样我们在类中设置资源时更方便资源的共享。
4.线程池
Executors工具类
在创建线程池时可以使用Executors工具类,其中Java为我们提供了四种常用的线程池构建方法
1.FixedThreadPool 固定线程数线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
从上面可以看出,这种线程池的corePoolSize等于maximumPoolSize,且keepAliveTime等于0,说明这种线程池在一开始就会创建固定数量的线程,在执行任务时线程数不会发生改变,有超出线程数的任务时会直接放入一个*队列中等待。
2.CachedThreadPool 缓存线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数为0,因此在初创或者长时间空闲时池中线程数为0,消耗的资源也会很少,最大线程数为Int的最大值基本上用不着这么多,空闲等待时间为一分钟,所以当线程结束自己的任务后会等待一分钟时间如果一分钟内没有任务才销毁线程,队列为SynchronousQueue这个队列一般用于任务交接每放入一个元素必须取出才能放入下一个。
3.SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
这个线程池只有一个线程,有多余任务时会放入等待队列中。
4.ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
可以看见这里创建了一个ScheduledThreadPoolExecutor,再跟进一步。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
这里的super构造方法是
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
所以其实这里还是创建的一个ThreadPoolExecutor,核心线程数可以自己根据实际情况设定,最大线程数是int的最大值一般不会超过,keepAliveTime是0,而等待队列是一个延时优先级队列,通过这个队列就可以实现延时或定时执行的效果,具体原理可以查找DelayedWorkQueue的相关博客。
5.WorkStealingPool(1.8新增)
这个线程池是ForkJoinPool的优化,实现算法是工作窃取算法。也就是说假设有三个线程thread1,thread2,thread3分别在执行各自的任务,如果thread3先执行完了自己的任务,则thread3会去窃取thread1或者thread2的工作。
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
这里创建的线程池发生了改变不再是ThreadPool线程池了,而是ForkJoinPool这是一个分治算法实现的线程池。
关于ForkJoinPool原理的学习可以
点击这里下面分别是各个线程池的创建和简单使用
SingleThreadExecutor
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Single Thread...");
}
});
FixedThreadPool
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Fixed Thread...");
}
});
CachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Cached Thread...");
}
});
ScheduledThreadPool
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("Scheduled Thread...");
}
}, 1L, TimeUnit.SECONDS);
Thread.sleep(500);
System.out.println("Start...");
WorkStealingPool
ExecutorService executorService = Executors.newWorkStealingPool();
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("Work Stealing Thread...");
}
});
这里只是简单提及下基本的方法,实际应用肯定复杂得多。