通常,最开始学Java时,最先接触到线程的就是 Thread 类和 Runnable接口。但是直接用这种方法来实现多线程是有弊端的
弊端:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
Java 线程池
Java通过Executors提供四种线程池,分别为:
1、newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2、newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3、newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4、newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
1.newCachedThreadPool
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并 从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService Pool= Executors.newCachedThreadPool();
for(int i = 0; i < 10; i++) {
Thread t = new MyThread(i);
Pool.execute(t);
}
Pool.shutdown();
}
}
class MyThread extends Thread {
private int index;
public MyThread(int index) {
this.index = index;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ": " + index);
Thread.sleep(2000 * index);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
执行结果:
pool-1-thread-3: 2
pool-1-thread-1: 0
pool-1-thread-2: 1
pool-1-thread-4: 3
pool-1-thread-5: 4
pool-1-thread-6: 5
pool-1-thread-7: 6
pool-1-thread-8: 7
pool-1-thread-9: 8
pool-1-thread-10: 9
本次执行的结果,一次性全部输出,可以看出线程容纳了全部十个线程。
2.newFixedThreadPool
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务, 则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何 线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之 前,池中的线程将一直存在。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService Pool= Executors.newFixedThreadPool(5);
for(int i = 0; i < 10; i++) {
Thread t = new MyThread();
Pool.execute(t);
}
Pool.shutdown();
}
}
class MyThread extends Thread {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
执行结果:
pool-1-thread-2
pool-1-thread-1
pool-1-thread-4
pool-1-thread-3
pool-1-thread-5
pool-1-thread-1
pool-1-thread-2
pool-1-thread-5
pool-1-thread-4
pool-1-thread-3
在执行的时候,会发现前五个是一块输出的,后五个是停了两秒才输出的,证明当线程数达到最大值时,加入的线程是不会运行的。
3、newScheduledThreadPool
创建一个固定大小的线程池。此线程池支持定时以及周期性执行任务的需求。
package demo;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) {
ScheduledExecutorService Pool = Executors.newScheduledThreadPool(5);
Thread t1 = new MyThread("嘿嘿");
// 延时任务
Pool.schedule(t1, 1, TimeUnit.SECONDS);
Thread t2 = new MyThread("哈哈");
// 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
Pool.scheduleAtFixedRate(t2, 1, 1, TimeUnit.SECONDS);
Thread t3 = new MyThread("哦哦");
// 循环任务,以上一次任务的结束时间计算下一次任务的开始时间
Pool.scheduleWithFixedDelay(t3, 1, 5, TimeUnit.SECONDS);
}
}
class MyThread extends Thread {
private String msg;
public MyThread(String msg) {
this.msg = msg;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ":" + msg);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
方法参数
1、schedule
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
command:执行线程
delay:初始化延时
unit:计时单位
2、scheduleAtFixedRate
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
command:执行线程
initialDelay:初始化延时
period:两次开始执行最小间隔时间
unit:计时单位
3、scheduleWithFixedDelay
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
command:执行线程
initialDelay:初始化延时
period:前一次执行结束到下一次执行开始的间隔时间(间隔执行延迟时间)
unit:计时单位
执行结果:
pool-1-thread-2:哈哈
pool-1-thread-1:嘿嘿
pool-1-thread-3:哦哦
pool-1-thread-1:哈哈
pool-1-thread-1:哈哈
pool-1-thread-5:哈哈
pool-1-thread-4:哦哦
pool-1-thread-3:哈哈
pool-1-thread-3:哈哈
pool-1-thread-3:哈哈
pool-1-thread-4:哦哦
pool-1-thread-3:哈哈
pool-1-thread-3:哈哈
pool-1-thread-4:哈哈
pool-1-thread-4:哈哈
pool-1-thread-2:哦哦
pool-1-thread-3:哈哈
pool-1-thread-3:哈哈
pool-1-thread-5:哈哈
通过结果可以看出,最多只创建了5个线程,schedule方法只执行了一次,其他两个方法每隔一段时间执行一次。