首页 > 代码库 > 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,
 */// :~


4、线程相关方法和知识

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)。





+待添加