首页 > 代码库 > JAVA学习笔记 -- 线程
JAVA学习笔记 -- 线程
之前学习的都是关于顺序编程的知识,程序在任意时刻都只能执行一个步骤。线程作为我接触并发编程的第一堂课,我感觉很兴奋。
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!), */// :~如上代码,从Runnable导出一个类,再调用它的run()方法,但这个方法不会产生任何内在的线程能力。
要实现线程行为,必须将它附着到线程上。
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!), */// :~在main()线程中,start()迅速的返回,此时子线程t执行的LiftOff.run()任务还未结束。这时两个线程并发运行,通过输出也可以看出。
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),后台线程派生出来的子线程都是后台线程。
e. join(),如果某个线程在另一个线程t上调用t.join(),此线程会被挂起,直到目标线程t结束才可恢复(即t.isAlive()返回为false)。
+待添加