private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {}
public void run() {
System.out.println("startThreadId: " + id);
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.println("endTHreadId: " + id);
}
}
运行结果:
startThreadId: 0
startThreadId: 1
startThreadId: 2
startThreadId: 3
Waiting for LiftOff
startThreadId: 4
endTHreadId: 0
endTHreadId: 1
endTHreadId: 2
endTHreadId: 3
endTHreadId: 4
运行的结果反映了多线程运行的时候线程的调度。调度的规则主要由线程管理器来决定,在多核心的计算机中,线程管理器会把线程交由不同的cpu来完成。
由于线程的调度机制并不是确定的,不同版本的jdk所得到的结果是不同的。
对于打印结果的理解:运行之后哦main线程尝试创建5个线程,但在创建#3进程的时候时间片用完了,main的Thread进入等待队列,接着时间片论转到最先进入线程队列的#0进程->#0时间片用完(线程还没销毁)->时间片轮转到#1->#1时间片用完....(看不懂的要回头学一下操作系统中进程管理的知识)。
四、使用Executors
线程池由 Executor 框架提供。 Executor 框架将处理请求任务的提交和它的执行解耦。可以制定执行策略。在线程池中执行线程可以重用已经存在的线程,而不是创建新的线程,可以在处理多请求时抵消线程创建、消亡产生的开销。
ExecutorService 的几个重要方法:
1、shutdown方法:这个方法会平滑地关闭ExecutorService,当我们调用这个方法时,ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。
2、awaitTermination方法:这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用
直接看代码:
[java]
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] arges)
{
//ExecutorService exec = Executors.newSingleThreadExecutor();
ExecutorService exec = Executors.newFixedThreadPool(3);
for(int i = 0; i <5; i++)
{
try{
exec.execute(new LiftOff());
//Thread.sleep(1000);
}catch(Exception e)
{
e.printStackTrace();
}
}
exec.shutdown();
System.out.println("Waiting for LiftOff");
}
}
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] arges)
{
//ExecutorService exec = Executors.newSingleThreadExecutor();
ExecutorService exec = Executors.newFixedThreadPool(3);
for(int i = 0; i <5; i++)
{
try{
exec.execute(new LiftOff());
//Thread.sleep(1000);
}catch(Exception e)
{
e.printStackTrace();
}
}
exec.shutdown();
System.out.println("Waiting for LiftOff");
}
}
运行结果:
startThreadId: 0
startThreadId: 1
Waiting for LiftOff
startThreadId: 2
endThreadId: 0
startThreadId: 3
endThreadId: 1
startThreadId: 4
endThreadId: 2
endThreadId: 3
endThreadId: 4
newCachedThreadPool()创建一个可根据需要创建新线程的线程池,newSingleThreadExecutor()约等于newFixedThreadPool(1)
SingleThreadExecutor运行结果:
Waiting for LiftOff
startThreadId: 0
endThreadId: 0
startThreadId: 1
endThreadId: 1
startThreadId: 2
endThreadId: 2
startThreadId: 3
endThreadId: 3
startThreadId: 4
endThreadId: 4
CachedThreadPool运行结果:
startThreadId: 0
startThreadId: 1
startThreadId: 2
startThreadId: 3
Waiting for LiftOff
startThreadId: 4
endThreadId: 0
endThreadId: 1
endThreadId: 2
endThreadId: 3
endThreadId: 4
Executors还是有点难理解,需要很多的实战才能有更好的体会,这里点到为止。
五、Callable 和 Future接口
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
Callable和Runnable有几点不同:
(1)Callable规定的方法是call(),而Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
(3)call()方法可抛出异常,而run()方法是不能抛出