JAVA的线程主要通过线程池管理,其主要的意义在于优雅的关闭线程以及合理地运用线程来处理我们需要执行的一些任务。换句话说就是:通过线程池,创建一定数量的线程,并交由线程池来管理。在当需要执行任务时,直接使用其中的线程。任务执行完成后,线程保留,并可用于执行下一个任务。如果任务比线程多,则任务会等待线程空闲出来时,使用空闲的线程来执行任务。
1、线程复用的实例
public class MyThread implements Runnable {
private String str;
public MyThread(String s){
this.str = s;
}
@Override
public void run() {
System.out.println(str+":"+Thread.currentThread().getName()+" is running!");
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] agrs){
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new MyThread("A"));
executorService.execute(new MyThread("B"));
executorService.execute(new MyThread("C"));
executorService.shutdown();
}
}
执行结果
A:pool-1-thread-1 is running!
B:pool-1-thread-2 is running!
C:pool-1-thread-1 is running!
线程池只分配了两个线程,这两个线程先执行了A和B线程,当线程A执行完毕后,就将空闲出来的线程用来执行线程C。
接下来来看Executors.newCachedThreadPool() 缓冲池的实例,缓冲池是根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。若创建的线程空闲60秒以上则将其销毁并移除。
下面直接上代码
public class MyThread implements Runnable {
private String str;
public MyThread(String s){
this.str = s;
}
@Override
public void run() {
try {
System.out.println(str + ":" + Thread.currentThread().getName() + " is running!");
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] agrs){
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new MyThread("A"));
executorService.execute(new MyThread("B"));
executorService.execute(new MyThread("C"));
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
executorService.execute(new MyThread("D"));
executorService.execute(new MyThread("E"));
executorService.shutdown();
}
}
执行结果
A:pool-1-thread-1 is running!
C:pool-1-thread-3 is running!
B:pool-1-thread-2 is running!
E:pool-1-thread-3 is running!
D:pool-1-thread-2 is running!
通过Executors.newCachedThreadPool() 创建了缓冲池,因为需要执行A,B,C三个线程,因此缓冲池创建了3个线程,这三个线程在启动后500毫秒就执行完毕,主线程等带1秒后,这时候缓冲池并没有将这三个线程销毁,也没有创建新的线程,直接将这三个空闲的线程用来执行D和E这两个线程。
2、Callable接口
Callable接口与Runnable接口相似,都用于定义线程的可执行任务。Callable在JDK 1.5引入,与Runnable相比,有三个优势:
1、可以在任务中抛出异常
2、可以终止任务
3、可以获取任务的返回值
这时候就会出现一个问题:新旧两种定义线程任务的方式,怎么前后兼容呢?
这时候就出现了FutureTask这个类
ublic class FutureTask<V> implements RunnableFuture<V> {
// 封装实现Callable接口的任务
public FutureTask(Callable<V> callable) { //...}
// 封装实现Runnable接口的任务,result是返回值,在任务完成后赋值(futureTask.get()会返回这个值)。
public FutureTask(Runnable runnable, V result) {//...}
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
只要将Callable或Runnable封装成FutureTask,即可在两种场景使用。
下面上一个代码实例:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception{
int num=0;
for(int i=0;i<3;i++){
num++;
System.out.println("num="+num);
}
return String.valueOf(num);
}
}
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] agrs){
FutureTask<String> task = new FutureTask<String>(new MyCallable());
new Thread(task).start();
System.out.println(getFutureValue(task));
System.out.println("after task.get()!");
}
public static String getFutureValue(FutureTask<String> task){
String str = "default value";
try{
str = task.get();
}catch (Exception e){
System.out.println("task is canceled!");
}
return str;
}
}
执行结果
num=1
num=2
num=3
3
after task.get()!
可以看到"after task.get()!"这句话在 getFutureValue()方法执行后即task.get()方法执行执行。
将代码改造一下
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println("num="+(i+1));
}
}
}
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] agrs){
String str = "default value";
FutureTask<String> task = new FutureTask<String>(new MyRunnable(),str);
new Thread(task).start();
System.out.println(getFutureValue(task));
System.out.println("after task.get()!");
}
public static String getFutureValue(FutureTask<String> task){
String str = "default value1";
try{
str = task.get();
}catch (Exception e){
System.out.println("task is canceled!");
}
return str;
}
}
执行结果
num=1
num=2
num=3
default value1
after task.get()!
可以看到,使用Runnable构造FutureTask时,future.get()直接得到defaultValue。
3、中断任务线程
中断Callable任务
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception{
int num=0;
for(int i=0;i<3;i++){
num++;
Thread.sleep(100);
System.out.println("num="+num);
}
return String.valueOf(num);
}
}
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] agrs){
FutureTask<String> task = new FutureTask<String>(new MyCallable());
new Thread(task).start();
try{
Thread.sleep(250);
}catch (InterruptedException e){
e.printStackTrace();
}
task.cancel(true);
System.out.println(getFutureValue(task));
System.out.println("after task.get()!");
}
public static String getFutureValue(FutureTask<String> task){
String str = "default value";
try{
str = task.get();
}catch (Exception e){
System.out.println("task is canceled!");
}
return str;
}
}
执行结果
num=1
num=2
task is canceled!
default value
after task.get()!
task.cancel()参数为true时,线程会立即中断,call()方法里面后续的部分也不会被执行
将task.cancel()参数置为false时的执行结果
num=1
num=2
task is canceled!
default value
after task.get()!
num=3
task.cancel()参数为true时,退出了task.get()方法的阻塞,但线程call()方法里面后续的部分会继续被执行完毕
中断Runnable任务
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<3;i++){
try {
Thread.sleep(100);
}catch (InterruptedException e){
System.out.println("task is cancel!");
}
System.out.println("num="+(i+1));
}
}
}
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] agrs){
String str = "default value1";
FutureTask<String> task = new FutureTask<String>(new MyRunnable(),str);
new Thread(task).start();
try{
Thread.sleep(250);
}catch (InterruptedException e){
e.printStackTrace();
}
task.cancel(false);
System.out.println(getFutureValue(task));
System.out.println("after task.get()!");
}
public static String getFutureValue(FutureTask<String> task){
String str = "default value";
try{
str = task.get();
}catch (Exception e){
System.out.println("task is canceled!");
}
return str;
}
}
task.cancel(false)的执行结果:
num=1
num=2
task is canceled!
default value
after task.get()!
num=3
task.cancel(true)的执行结果
num=1
num=2
task is cancel!
num=3
task is canceled!
default value
after task.get()!
可以看到task.cancel()无论将参数置为true还是false,线程都不会被执行完毕,但置为true时,执行结果多了条“task id cancel!”,说明置为true时,run()方法确实放生了中断异常。
如果确实要中断Runnable使得run()方法在中断后不继续执行可以将run()方法改造成下面这样(即在发生中断异常时,将run()方法立即返回):
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<3;i++){
try {
Thread.sleep(100);
}catch (InterruptedException e){
System.out.println("task is cancel!");
return;
}
System.out.println("num="+(i+1));
}
}
}
这时候的直接结果:
num=1
num=2
task is cancel!
task is canceled!
default value
after task.get()!
4、线程池管理线程任务
通过ExecutorService,可以使用4个方法执行线程任务。
其中3个执行Runnable任务:
void execute(Runnable command);
< T > Future< T > submit(Runnable task, T result);
Future< ? > submit(Runnable task);
1个执行Callable任务:
< T > Future< T > submit(Callable< T > task); //执行Callable任务
只有通过submit方法执行的线程任务,才能获取到Future,才能通过Future管理线程。
示例代码如下:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception{
int num=0;
for(int i=0;i<3;i++){
num++;
Thread.sleep(100);
System.out.println("num="+num);
}
return String.valueOf(num);
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test {
public static void main(String[] agrs){
ExecutorService service = Executors.newFixedThreadPool(2);
Future<String> future = service.submit(new MyCallable()); // 使用submit()执行Callable任务
System.out.println("执行结果:"+getFutureValue(future));
System.out.println("after task.get()!");
service.shutdown();
}
public static String getFutureValue(Future<String> task){
String str = "default value";
try{
str = task.get();
}catch (Exception e){
System.out.println("task is canceled!");
}
return str;
}
}
执行结果
num=1
num=2
num=3
执行结果:3
after task.get()!
和Callable接口的第一个实例一致,能够通过Future获取执行的结果
5、利用后台线程管理任务
Thread类提供了一个setDaemon方法,可以将线程设置成后台(守护)线程,当宿主线程任务完成后,后台线程自动终止。
直接上代码实例:
public class TestThead implements Runnable {
private void printNum(int num){
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(num);
}
@Override
public void run() {
Thread deamon = new Thread(()->{
for(int i=5;i<10;i++){
printNum(i);
}
});
deamon.setDaemon(true);
deamon.start();
for(int i=0;i<2;i++){
printNum(i);
}
}
public static void main(String[] args){
new Thread(new TestThead()).start();
}
}
执行结果:
5
0
6
1
后台线程只打印了5,6,在主线程循环执行完毕并结束后,后台线程也跟着结束了,这验证了后台线程可以随着主线程而终止的功能