1.基本线程机制
并发编程使我们可以将程序划分为多个分离的,独立运行的任务。通过使用多线程机制,这些独立任务中的每一个都将由执行线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,因此,单个进程可以拥有多个并发执行的任务。
2.Java中如何编写多线程
2.1 定义任务
下面的LiftOff任务监视发射之前的倒计时:
public class LiftOff implements Runnable {
protected int countDown = 10; // 默认从10开始倒计时
private static int taskCount = 0;//总线程的个数
private final int id = taskCount++;//线程的ID
public LiftOff() {}
public LiftOff(int countDown) {
this.countDown = countDown;
}
//显示当前线程ID和倒计时数
public String status() {
return "#" + id + "(" +
(countDown > 0 ? countDown : "Liftoff!") + "), ";
}
public void run() {
while(countDown-- > 0) {
System.out.print(status());
Thread.yield();
}
}
}
其中的Thread.yield()的调用是对线程调度器的一种建议。它在声明:”我已经完成了任务,此刻是切换给其他任务一段时间的大好时机.”
2.2 Thread类
Thread构造器只需要一个Runnable对象.
public class BasicThreads {
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方法本身就是一个线程,当在创建一个线程的时候,程序会同时运行两个方法,main()和LiftOff.run().
2.3 Executor
JavaSE5的java.util.concurrent中的执行器(Executor)将为你管理Thread对象,从而简化并发编程.Executor是启动任务的优选方法.
public class CachedThreadPool {
public static void main(String[] args) {
//创建缓存线程池
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++)
//将任务加入到Executor管理
exec.execute(new LiftOff());
exec.shutdown();
}
}
//output
#4(9), #4(8), #4(7), #4(6), #3(9), #3(8), #1(9), #2(9), #0(9), #0(8), #2(8), #1(8), #3(7), #4(5), #3(6), #1(7), #2(7), #0(7), #2(6), #1(6), #3(5), #4(4), #3(4), #1(5), #2(5), #0(6), #2(4), #1(4), #3(3), #4(3), #4(2), #4(1), #3(2), #4(Liftoff!), #1(3), #1(2), #1(1), #1(Liftoff!), #2(3), #0(5), #2(2), #3(1), #2(1), #0(4), #2(Liftoff!), #3(Liftoff!), #0(3), #0(2), #0(1), #0(Liftoff!),
在这里使用的CachedThreadPool在程序执行过程中通常会创建于所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是Executor的首选.
除了CachedThreadPool,还有FixedThreadPool(一次性执行代价高昂的线程分配)和SingleThreadExecutor(所有任务将使用相同线程).
2.4 任务中返回值
实现Callable接口
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 CallableDemo {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
//记录线程的返回Future对象
ArrayList<Future<String>> results =
new ArrayList<Future<String>>();
for(int i = 0; i < 10; i++)
results.add(exec.submit(new TaskWithResult(i)));
for(Future<String> fs : results)
try {
// 得到返回值
System.out.println(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
如果Future任务没有完成,get()方法将堵塞. exec.shutdown()方法的调用可以防止新任务被提交给这个Executor.
2.5 优先级
public class SimplePriorities implements Runnable {
//倒计时值
private int countDown = 5;
//原子性的double类型
private volatile double d; // No optimization
//优先级
private int priority;
public SimplePriorities(int priority) {
this.priority = priority;
}
//输出当前线程+递减值
public String toString() {
return Thread.currentThread() + ": " + countDown;
}
public void run() {
//设置优先级
Thread.currentThread().setPriority(priority);
while(true) {
// 执行耗时操作
for(int i = 1; i < 100000; i++) {
d += (Math.PI + Math.E) / (double)i;
if(i % 1000 == 0)
Thread.yield();
}
//输出
System.out.println(this);
if(--countDown == 0) return;
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++)
exec.execute(
//设置最低优先级
new SimplePriorities(Thread.MIN_PRIORITY));
exec.execute(
//设置最高优先级
new SimplePriorities(Thread.MAX_PRIORITY));
exec.shutdown();
}
}
//output
Thread[pool-1-thread-3,1,main]: 5
Thread[pool-1-thread-6,10,main]: 5
Thread[pool-1-thread-2,1,main]: 5
Thread[pool-1-thread-3,1,main]: 4
Thread[pool-1-thread-1,1,main]: 5
Thread[pool-1-thread-4,1,main]: 5
Thread[pool-1-thread-5,1,main]: 5
Thread[pool-1-thread-6,10,main]: 4
Thread[pool-1-thread-3,1,main]: 3
Thread[pool-1-thread-6,10,main]: 3
Thread[pool-1-thread-2,1,main]: 4
Thread[pool-1-thread-3,1,main]: 2
Thread[pool-1-thread-1,1,main]: 4
Thread[pool-1-thread-6,10,main]: 2
Thread[pool-1-thread-5,1,main]: 4
Thread[pool-1-thread-4,1,main]: 4
Thread[pool-1-thread-3,1,main]: 1
Thread[pool-1-thread-6,10,main]: 1
Thread[pool-1-thread-2,1,main]: 3
Thread[pool-1-thread-4,1,main]: 3
Thread[pool-1-thread-5,1,main]: 3
Thread[pool-1-thread-1,1,main]: 3
...
...
pool-1-thread-6为高优先级线程.
在这里可以看出,就算设置了高的优先级,也只是使最高优先级被选择的概率增高,并不会只先执行优先级高的线程.
2.6 后台线程
public class SimpleDaemons implements Runnable {
public void run() {
try {
while(true) {
//线程睡眠
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() + " " + this);
}
} catch(InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
public static void main(String[] args) throws Exception {
for(int i = 0; i < 10; i++) {
Thread daemon = new Thread(new SimpleDaemons());
//设置为后台线程,该方法必须在start()执行
daemon.setDaemon(true); // Must call before start()
daemon.start();
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(175);
}
}
//output
All daemons started
Thread[Thread-1,5,main] com.praysz.thread.SimpleDaemons@126be4cc
Thread[Thread-8,5,main] com.praysz.thread.SimpleDaemons@4e5ced83
Thread[Thread-7,5,main] com.praysz.thread.SimpleDaemons@36ed5ba6
Thread[Thread-2,5,main] com.praysz.thread.SimpleDaemons@697a1686
Thread[Thread-4,5,main] com.praysz.thread.SimpleDaemons@697a1686
Thread[Thread-0,5,main] com.praysz.thread.SimpleDaemons@126be4cc
Thread[Thread-6,5,main]
...
...
JavaSE5引入了更加显示的sleep版本,作为TimeUnit类的一部分.
当把main方法中的最后TimeUnit.MILLISECONDS.sleep(175)去除后
//output
All daemons started
这说明,当最后一个非后台线程终止时,后台线程会”突然终止”,因此一旦main()方法退出,JVM会立即关闭所有的后台进程.