初学者的疑惑:线程是一个任务?错。
在Java中,Thread类自身不执行任何操作,它只是驱动赋予它的任务,但是线程研究中总是使用"线程执行这项或那项任务"这样的言语,因此给人强烈的错觉是线程就是一个任务,其实不然。从概念上说,我们希望创建独立于其他任务的任务,因此我们应该可以定义任务,然后说"开始执行",并且不用操心其细节。但是在物理上,创建线程可能会代价高昂,因此你必须保存并管理他们。从实现的角度看,将任务从线程中分离出来是很有意义的。另外,程序员也是要面子的,我们已经对Thread类没有任何控制权,如果连任务的具体定义的资格都没有,那线程也就没有存在的必要了,因为那是自取其辱。另外,Java的线程机制来自于C的低级的P线程方式。这是一种你必须深入研究,并且需要完全理解其所有事物的所有细节的方式。
定义任务:
线程需要驱动任务(其实是驱动实现了Runnable接口的类的对象),因此需要一种描述任务的方式,而Runnable接口可以提供。要想定义任务,只需实现Runnable接口并实现run()方法,使得该任务可以执行你的命令。
因此,Run方法才是真正的任务。
当从Runnable中导出一个类时,它必须具有run()方法,但是这个方法并无特殊之处——它不会产生任何内在的线程能力,要实现线程行为,必须显示的将一个任务附着到线程上。
线程驱动方式:
1)直接通过Thread来驱动
Thread构造器只需要一个Runnable对象。调用Thread对象的start()方法为该线程进行必要的初始化操作,然后才是调用Runnable()的run()方法,以便在新线程中启动该任务。
下面的示例中需要注意的是,虽然start()方法迅速返回了,但是后面的输出语句先执行,是因为除了 t 线程之外,还有一个main()主线程,此时main()主线程拥有系统时间片,所以先执行后面的语句,然后 t 线程获取到系统时间片,继续调用run()方法。
class LiftOff implements Runnable {
public void run() {
System.out.println("定义任务");
}
}
public class BasicThreads {
public static void main(String[] args) {
Thread t = new Thread(new LiftOff());
t.start();
System.out.println("先于run()方法输出");
}
}
当然,你可以添加更多的线程去驱动更多的任务,如下:
class LiftOff1 implements Runnable {
protected int countDown = 10;
private static int taskCount = 0 ;
private final int id = taskCount++;
public String status() {
return "#" + id + "(" + (countDown > 0 ? countDown : "LiftOff") + ")";
}
public void run() {
while(countDown-- >0) {
System.out.println(status());
Thread.yield();
}
}
}
public class MoreBasicThreadS {
public static void main(String[] args) {
for (int i = 0; i <5 ; i++)
new Thread(new LiftOff1()).start();
System.out.println("Waiting for LiftOff");
}
}
Thread.yield():
一般用在当前线程主要任务已经完成时,将自己从运行状态转化为就绪状态,希望可以切换执行其他线程,但是需要注意,该线程下次仍然有可能被分配到系统时间片。所以这仅是一个好的初衷,是否实现,随缘。
此外,较早的JDK不会频繁对时间切片,某个线程可能会先循环到尽头,再开始下个线程的执行。较晚的JDK看起来有更好的时间切片行为,让各个线程执行的更加"均匀"。
2)使用Executor
Java SE5的java.util.concurrent包中的执行器Executor可以帮助我们管理Thread对象,大大简化并发编程。Executor在客户端和任务执行之间提供了一个间接层,与客户端直接执行任务不同,这个中介对象将执行任务。
Executor、ExecutorService、Executors:
区别:
- ExecutorService 接口继承了 Executor 接口,是 Executor 的子接口。
- Executor 接口定义了 execute()方法用来接收一个Runnable接口的对象,而 ExecutorService 接口中的 submit()方法可以接受Runnable和Callable接口的对象。
- Executor 中的 execute()方法不返回任何结果,而 ExecutorService 中的submit()方法可以通过一个 Future 对象返回运算结果。
- Executor 和 ExecutorService 接口都允许客户端提交一个任务,但ExecutorService 还提供用来控制线程池的方法。比如:调用 shutDown()方法终止线程池。
- Executors 是一个类,提供工厂方法用来创建不同类型的线程池。比如:newSingleThreadExecutor()创建一个只有一个线程的线程池,newFixedThreadPool(int numOfThreads)来创建固定线程数的线程池,newCachedThreadPool()可以根据需要创建新的线程,但如果已有线程是空闲的会重用已有线程。