之前学习的都是关于顺序编程的知识,程序在任意时刻都只能执行一个步骤。线程作为我接触并发编程的第一堂课,我感觉很兴奋。
1、定义任务
实现Runnable接口并编写run()方法(线程将会执行此方法内代码)。
- class LiftOff implements Runnable {
- protected int countDown = 10;
- private static int taskCount = 0;
- private final int id = taskCount++;
- public LiftOff() {
- }
- public LiftOff(int countDown) {
- this.countDown = countDown;
- }
- public String status() {
- return "#" + id + "("
- + (countDown > 0 ? String.valueOf(countDown) : "Liftoff!")
- + "),";
- }
- public void run() {
- while (countDown-- > 0) {
- System.out.print(status());
- Thread.yield();
- }
- System.out.println();
- }
- }
- public class ThreadTest {
- public static void main(String[] args) {
- LiftOff launch = new LiftOff();
- launch.run();
- }
- }/*
- * Output: #0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(Liftoff!),
- */// :~
要实现线程行为,必须将它附着到线程上。
2、创建和启动线程
A. 通过Thread构造器。将Runnable对象提交给一个Thread构造器,再调用Thread对象的start()方法启动线程。
- public class ThreadTest {
- public static void main(String[] args) {
- Thread t = new Thread(new LiftOff());
- t.start();
- System.out.println("Waiting for LiftOff");
- }
- }/*
- * Output: Waiting for LiftOff
- * #0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(Liftoff!),
- */// :~
B. 使用Executor。Executor是java.util.concurrent包中的执行器,可以更好的管理Thread对象。
a. CachedThreadPool,为每个任务都创建一个线程。
b. FixedThreadPool,使用有限的线程集来执行所提交的任务。
c. SingeThreadExecutor,如线程数量为1的FixedThreadPool。
- public class ThreadTest {
- public static void main(String[] args) {
- ExecutorService exec1 = Executors.newCachedThreadPool();
- ExecutorService exec2 = Executors.newFixedThreadPool(5);//线程集限制数目为5
- ExecutorService exec3 = Executors.newSingleThreadExecutor();
- exec1.execute(new LiftOff());
- exec1.shutdown();//防止新任务被提交给这个Executors
- }
- }
3、Callable接口
Runnable是执行工作的独立任务,但它没有返回值。而Callable可以从任务中产生返回值。
下面代码展示它的用法:
- import java.util.concurrent.*;
- import java.util.*;
- class TaskWithResult implements Callable<String> {
- private int id;
- public TaskWithResult(int id) {
- this.id = id;
- }
- public String call() {
- return "result of TaskWithResult " + id;
- }
- }
- public class ThreadTest {
- public static void main(String[] args) {
- ExecutorService exec = Executors.newCachedThreadPool();
- ArrayList<Future<String>> results = new ArrayList<Future<String>>();
- for (int i = 0; i < 10; i++) {
- results.add(exec.submit(new TaskWithResult(i)));
- // Callable的调用方式必须是 ExecutorService.submit(),
- // 同时submit()会产生一个Future对象,可以通过isDone()查询Future是否完成,通过get()获取call()的返回值.
- }
- for (Future<String> fs : results)
- try {
- System.out.print(fs.get() + ",");
- } catch (InterruptedException e) {
- System.out.println(e);
- return;
- } catch (ExecutionException e) {
- System.out.println(e);
- } finally {
- exec.shutdown();
- }
- }
- }/*
- * Output: result of TaskWithResult 0,result of TaskWithResult 1,result of
- * TaskWithResult 2,result of TaskWithResult 3,result of TaskWithResult 4,result
- * of TaskWithResult 5,result of TaskWithResult 6,result of TaskWithResult
- * 7,result of TaskWithResult 8,result of TaskWithResult 9,
- */// :~
a. yield(),表示当前线程已经完成或暂时不需要CPU,其它线程可以占用之,这只是个建议。
b. 优先级,通过getPriority()和setPriority()方法获取和重置线程的优先级,优先级和多数操作系统都不能映射的很好,唯一可移植的是设置优先级时,只使用 MAX_PRIORITY、NORM_PRIORITY和MIN_PRIORITY三个级别。
c. deamon(后台线程),它是程序运行的时候在后台提供的一种通用服务的线程,当所有非后台线程结束时,程序会杀死所有后台线程。
设置成后台线程的方法是:必须在线程启动前调用 setDaemon(true),后台线程派生出来的子线程都是后台线程。
d. join(),如果某个线程在另一个线程t上调用t.join(),此线程会被挂起,直到目标线程t结束才可恢复(即t.isAlive()返回为false)。