概要:对于线程理解一直的不深,这篇文章参考《java编程思想》并发这一章节的思路,稍微深入的梳理线程知识,例子来源主要是书上的练习题。不过由于原书篇幅较长,所以这篇文章主要梳理基本的线程机制这一小节。
- 定义任务
练习一:实现一个Runnable接口。在run()内部打印一个消息,然后调用yield()。重复这个操作三次,然后从run()中返回。在构造器中放置一条启动消息,并且放置一条在任务终止时的关闭消息。使用线程创建大量的任务并驱动它们。
package twentyoneThread;
//实现Runnable接口
class ExcOne implements Runnable {
private int countDown = 3;
private static int taskCount = 0;
private final int id = taskCount++;
//在构造器中放置一条启动消息
public ExcOne() {
System.out.println(this+ "start" +id);
}
//run()内部打印一条消息
public String Status() {
return "#" + id + "(" + (countDown > 0 ? countDown : "ExcOneOff!") + ").";
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.println(Status()); //run()打印消息
Thread.yield();//调用yield()
}
}
}
public class TestExcOne {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new ExcOne()).start();
}
}
}
输出式样:
twentyoneThread.ExcOne@15db9742start0
twentyoneThread.ExcOne@6d06d69cstart1
twentyoneThread.ExcOne@7852e922start2
twentyoneThread.ExcOne@4e25154fstart3
twentyoneThread.ExcOne@70dea4estart4
#1(2).
#1(1).
#3(2).
#0(2).
#1(ExcOneOff!).
#2(2).
#3(1).
#4(2).
#0(1).
#2(1).
#4(1).
#0(ExcOneOff!).
#2(ExcOneOff!).
#4(ExcOneOff!).
#3(ExcOneOff!).
练习2:遵循generic/Fibonacci.java 的形式,创建一个任务,它可以产生由n个斐波那契数字组成的序列,其中n是通过任务的构造器而提供的。使用线程创建大量的这种任务并驱动它们。
package twentyoneThread;
import java.util.Arrays;
class ExcTwo implements Runnable {
private final int n;
//构造器传入n
public ExcTwo(int n) {
this.n = n;
}
//斐波那契数
private int getFibo(int n) {
if (n == 1 || n == 2)
return 1;
else
return getFibo(n - 1) + getFibo(n - 2);
}
//生成斐波那契数组
private int[] getFiboArray() {
int[] fibos = new int[n];
for (int i = 0; i < fibos.length; i++) {
fibos[i] = getFibo(i+1);
}
return fibos;
}
@Override
public void run() {
//打印斐波那契数组
System.out.format("Fibo of %d : %s \n", n, Arrays.toString(getFiboArray()));
}
}
public class TestExcTwo {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new ExcTwo(i+1)).start();
}
}
}
输出式样
Fibo of 2 : [1, 1]
Fibo of 4 : [1, 1, 2, 3]
Fibo of 5 : [1, 1, 2, 3, 5]
Fibo of 3 : [1, 1, 2]
Fibo of 1 : [1]
- 使用Executor
本段主要讨论FixedThreadPool,CachedThreadPool,SingleThreadExcutor三种形式。
FixedThreadPool可以一次性预先执行高昂的线程分配,因而也就可以限制线程的数量了。这样可以节约时 间,因为你不用为每个任务都固定的付出创建线程的开销。
CachedThreadPool在执行程序过程中通常会创建与所需要量相同的线程,然后在它回收旧线程时停止新建 线程,因此它是合理的Excutor的首选。
SingleThreadExcutor就像线程数量为1的FixedThreadPool。对于你希望在另一个线程中连续的运行的任何事物(长期存活的任务)来说,都是很有用的。
练习3: 使用各种不同类型的执行器重复练习1.
//三个线程池的测试方法为了方便就放一块了,如果想要得到正确的结果只能单独的跑,合在一起有问题,目前还不会解决,等以后对线程加深理解后再修改。
package twentyoneThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class ExcThree implements Runnable {
private int countDown = 3;
private static int taskCount = 0;
private final int id = taskCount++;
public String Status() {
return "#" + id + "(" + (countDown > 0 ? countDown : "ExcOneOff!") + ").";
}
public ExcThree() {
System.out.println(this + "start" + id);
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.println(Status());
Thread.yield();
}
}
}
public class TestExcThree {
public static void main(String[] args) {
//CachedThreadPool
ExecutorService exec1 = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
exec1.execute(new ExcThree());
}
exec1.shutdown();
System.out.println("------------------------------");
////FixedThreadPool
ExecutorService exec2 = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
exec2.execute(new ExcThree());
}
exec2.shutdown();
//SingleThreadExcutor
System.out.println("------------------------------");
ExecutorService exec3 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
exec3.execute(new ExcThree());
}
exec3.shutdown();
}
}
- 从任务中产生返回值
Runnable是执行工作的独立任务,但是它不会返回任何值。如果你希望任务在完成时能够返回一个值,那么必须实现Callable接口而不是Runnable接口。
练习4:修改练习2,使得计算所有斐波那契赎罪的任务成为Callble,创建多个任务并显示结果.
//原书将斐波那契数字改为求和
package twentyoneThread;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
//继承Callable接口
class ExcFour implements Callable<String> {
private final int n;
public ExcFour(int n) {
this.n = n;
}
private int getFibo(int n) {
if (n == 1 || n == 2)
return 1;
else
return getFibo(n - 1) + getFibo(n - 2);
}
private int[] getFiboArray() {
int[] fibos = new int[n];
for (int i = 0; i < fibos.length; i++) {
fibos[i] = getFibo(i + 1);
}
return fibos;
}
//调用call()方法
@Override
public String call() {
return "Fibo of" + n + ":" + Arrays.toString(getFiboArray());
}
}
public class TestExcFour {
public static void main(String[] args) {
//future对象的list
ArrayList<Future<String>> lists = new ArrayList<Future<String>>();
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
//调用submit方法
lists.add(exec.submit(new ExcFour(i + 1)));
}
for (Future<String> future : lists) {
try {
System.out.println(future.get());//调用get方法
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
exec.shutdown();
}
}
}
}
总结:篇幅貌似也不少,剩下的明天在写,最后做一个总结吧,本文主要讲述三个方面知识:线程的创建、多线程、Callable接口。具体定义及文字方面的详解参考《java编程思想》。