刚好学习Java使用多线程,标准使用是这样的:首先定义一个任务,再使用一个线程去执行这个任务。那么如果不用线程去执行任务或者用线程执行一个没有定义的任务会是什么情况呢?试一试看看
为了方便查看关系,定义一个显示字符的任务类,再定义另一个主线程类,用这个类的main函数去跑。
1. 定义任务 + 不用Thread跑
任务代码:
public class showInfo implements Runnable {
private String text;
private int times;
public showInfo(String t, int times) {
this.text = t;
this.times = times;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (times-- > 0) {
System.out.print(text);
}
}
}
主线程类:
public class MainThread {
public static void main(String[] args) {
showInfo s1 = new showInfo("hello.", 2);
showInfo s2 = new showInfo("world.", 3);
s1.run();
s2.run();
}
}
结果:
hello.hello.world.world.world.
可以看到,没有用Thread跑,其实定义了Runnable接口也没用,直接去跑还是在主线程里面的,顺序执行。
2. 定义任务 + 用Thread跑
任务不变,新的主线程类:
public class MainThread {
public static void main(String[] args) {
showInfo s1 = new showInfo("hello.", 2);
showInfo s2 = new showInfo("world.", 3);
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s2);
t1.start();
t2.start();
System.out.println("waiting for show:");
}
}
结果:
hello.waiting for show:
hello.world.world.world.
整体都错开,CPU时间分片处理效果就出来了。
3. 不实现Runnable接口 + 用Thread跑
如果不实现Runnable接口,Eclipse里面会在Thread创建的时候报错,So。。。
使用线程,就是如此简单,当然,书上提到一个
hello.waiting for show:
world.hello.world.world.
第二排第一个变成了world但是这跟使用yield关系应该不是那么直接,多执行几次,结果还是会变化回去,看来线程分片有些随机性。
public class showInfo implements Runnable {
private String text;
private String name;
private int times;
public showInfo(String t, int times, String name) {
this.text = t;
this.times = times;
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(name + ": Thread running.");
while (times-- > 0) {
System.out.println(text);
Thread.yield();
}
System.out.println(name + ": Thread closing.");
}
}
在主线程类中一样跑,跑了很多次结果很随机,其中一次如下:
waiting for show:
Thread2: Thread running.
Thread1: Thread running.
hello.
world.
hello.
world.
Thread1: Thread closing.
world.
Thread2: Thread closing.
5. Executor
可以管理Thread对象,实际使用能够中,可以用它来创建和管理所有的任务
ExecutorService exec = Executors.newCachedThreadpool();
用来动态加入线程。
ExecutorService exec = Executors.newFixedThreadpool(n);
固定只能加入5个线程,节约资源。多余线程会排队。
exec.shutdown();
用于防止新的线程加入。
6. 从任务中产生返回值
那么就不能使用Runnable了,应该转换为Callable接口,方法call(),必须使用方法Executor.submit()来调用
创建一个待返回值的任务,计算斐波那契数列的和,这个应该能计算很久:
import java.util.concurrent.Callable;
public class FibnacciSum implements Callable<String> {
private int n;
final private int UPPER = 100000000;
FibnacciSum(int n){
this.n = n;
}
public String call() {
// TODO Auto-generated method stub
int a = 0;
int b = 1;
int t = 0;
for (int i = 0; i < UPPER; i++) {
t = b;
b = a + b;
a = t;
}
return n + ":Fib result:" + a;
}
}
在主线程类中用Executor创建10个任务,并且不断查询结果:
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MainThread2 {
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 FibnacciSum(i)));
for (Future<String> fs : results)
try{
while (!fs.isDone()) {
System.out.println("one not done");
}
System.out.println(fs.get());
} catch(InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
System.out.println(e);
} finally {
exec.shutdown();
}
System.out.println("waiting for show:");
}
}
这里就查了其中一个结果的值,从显示中可以看出,实现了Callable接口的任务返回一个Future<String>,通过这个对象可以查询对象是否已经准备好,准备好后即可调用。
可以这样: 不断循环查询,哪个好了先显示哪个,最后的结果一定是乱序的。
7. 优先级
任务类中定义 private int priority , 构建任务是写入优先级即可,比如: Thread.MIN_PRIORITY和Thread.MAX_PRIORITY,以及Thread.NORMAL_PRIORITY,由于优先级与系统映射不是很好,所以推荐使用这几个就可以了。
8. 后台线程