new Thread 创建线程:
new Thread(new Runnable(){
@override
public void run(){
}
}).start()
new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
使用线程池的原因:
创建和销毁线程资源消耗大,在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。
创建线程池的好处:
因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
风险:死锁、资源不足、并发、线程泄漏、请求过载。
常用的线程池
1)可缓存线程池(newCachedThreadPool)
如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
特点:工作线程创建的数量控制不强,最多小于设置的最大数目即可。如果长时间没有向线程池中提交任务,空闲的线程(有时间控制)将自动终止,当有任务需要且没有可用线程时则线程池重新创建一个新的工作线程。
注意:控制任务数量,大量线程同时运行可能会造成系统瘫痪。
2)指定工作线程数量的线程池(newFixedThreadPool)
特点:每提交一个新任务都会创建一个新的工作线程,直到达到定义的最大线程数,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
注意:在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
3)单工作线程(newSingleThreadExecutor)
特点:任何时候都只有一个线程在工作,保证按指定顺序执行各项任务。当工作线程异常结束, 会有另外一个线程代替它顺序执行任务。
4)定长线程池,支持定时周期执行任务(newScheduleThreadPool)
一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。
测试代码
package com.kjj.aboutThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 可缓存线程池练习
* @author 康晶晶
*
*/
public class CashThreadPoolTest {
/**
* 执行结果说明:依次按顺序答应0-9,每个数字有时间间隔
* @param args
*/
public static void main(String[] args) {
//创建一个缓存线程池
ExecutorService cashThreadPool =Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
//线程休眠
Thread.sleep(i*1000);
} catch (Exception e) {
e.printStackTrace();
}
//执行线程
cashThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
}
}
package com.kjj.aboutThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 定长线程池练习
* @author 康晶晶
*
*/
public class FixedThreadPoolTest {
/**
* 执行结果说明:线程池的大小是3,结果每次答应三个数,中间间歇2s,按组打印,顺序不一定顺序 0-2,3-5,6-8,9 处理完成后线程池不释放
* @param args
*/
public static void main(String[] args) {
ExecutorService fixedThread = Executors.newFixedThreadPool(3);
//根据系统资源进行设置线程池的大小
//ExecutorService fixedThread = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 10; i++) {
final int index = i;
//调用线程
fixedThread.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
package com.kjj.aboutThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单线程
* @author 康晶晶
*
*/
public class SingleThreadExecutorTest {
/**
* 执行结果描述:单个线程执行任务,按顺序一次打印0-9,每个数字间间隔2s
* @param args
*/
public static void main(String[] args) {
ExecutorService singleThread = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThread.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
package com.kjj.aboutThread;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 定长线程池,支持定时和周期执行任务
* @author 康晶晶
*
*/
public class ScheduleThreadPoolTest {
/**
* 执行结果:
* 1)定时测试:程序执行,等待设定的时间后同时答应0-9,无序
* 2)定时周期测试:程序执行,等待1s后执行答应操作3条一组,3秒后打印第二组,周期下去
* @param args
*/
public static void main(String[] args) {
ScheduledExecutorService schedualThread = Executors.newScheduledThreadPool(3);
//延迟执行练习 延迟3s执行
// for (int i = 0; i < 10; i++) {
// final int index = i;
// schedualThread.schedule(new Runnable() {
// @Override
// public void run() {
// System.out.println(index);
// }
// }, 3, TimeUnit.SECONDS);
// }
//周期执行测试 延迟1s,3s一个周期
schedualThread.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(i+"i...");
}
}
}, 1, 3, TimeUnit.SECONDS);
}
}
【ExecutorService 方法比较】
1-submit() 与 execute()区别
submit() 可接收 runnable 和 callable 参数,结果有返回值,方便Exception处理,出现异常可以及时捕获并处理,线程处理终止
execute()只接收 runnable , 结果没有返回值
2-shutdown()与 shutdownnow()区别
shutdown() 等待当前任务执行完成后再停止,不接受新的任务
shutdownnow() 不接受新的任务,试图停止执行当前的任务
3-runnable()与 callable()区别
runnable()没有返回值,可用于并发性任务执行
callable()有范型返回值,可用于顺序性任务执行(如 斐波那契数列的计算)