并发(一)
这个月,总结一下thread的相关知识,把基础知识弄得更牢固一下,学习先增加深度在扩展广度。
声明:文章内容以及部分代码示例,参考了《Thinking in Java》(Java编程思想)这本书。
该书绝对是java从业人员的宝典之一。强烈推荐。
1 并发
并发通常是提高运行在单处理器上的程序的性能。
一般它或提高程序执行效率,或为设计某些类型的程序提供更易用的模块,或两者都有。
在这里说一下线程安全的概念,多线程访问同一代码,不会产生不确定的结果,这样的话我们就说线程是安全的。
2 基本线程机制
2.1 定义任务,创建线程
一般是两种方式,一是继承一个thread类,另一种是实现Runnable接口。
下面给出代码示例:
1 public class LiftOff implements Runnable{ 2 3 protected int countDown = 10; 4 private static int taskCount = 0; 5 // 标识符id可以用来区分任务的多个实例,它是final的, 6 // 因为它一旦被初始化之后就不希望被修改 7 private final int id = taskCount++; 8 public LiftOff() {} 9 public LiftOff(int countDown) { 10 11 } 12 public String status() { 13 return "#" + id + "(" + 14 (countDown > 0 ? countDown : "LiftOff") + ")."; 15 } 16 public void run() { 17 18 while(countDown-- > 0){ 19 System.out.println(status()); 20 Thread.yield(); 21 } 22 } 23 }
1 public class ThreadDemo01 extends Thread{ 2 public static void main(String args[]){ 3 LiftOff lift = new LiftOff(); 4 lift.run(); 5 } 6 }
1 public class ThreadDemo02 extends Thread{ 2 public static void main(String args[]){ 3 Thread thread = new Thread(new LiftOff()); 4 thread.start(); 5 } 6 7 }
执行结果如下:
#0(9). #0(8). #0(7). #0(6). #0(5). #0(4). #0(3). #0(2). #0(1). #0(LiftOff).
大家查看API会发现,Thread类继承了Runnable接口,所以,线程最终是通过Runnable接口来实现的。推荐使用Runnable接口的方式,因为这样它还可以继承其它的类,增加自身的灵活性。
下面给出一个驱动多线程的示例:
1 public class ThreadDemo03 { 2 3 public static void main(String[] args) { 4 5 for (int i = 0; i < 5; i++) { 6 new Thread(new LiftOff()).start(); 7 } 8 } 9 10 }
2.2 使用Executor
书中写明,在JAVA 6中,Executor是启动任务的优选方法,但是目前在JAVA 8中,是不是优选,我不知道。
Executor相当于客户端和线程之间的一个桥梁,由它执行任务。
常用的Executor有newCachedThreadPool(首选), FixedThreadPool(需指定线程数量), SingleThreadExcutor等。
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 4 public class ThreadDemo04 { 5 public static void main(String[] args) { 6 7 ExecutorService service = Executors.newCachedThreadPool(); 8 9 // ExecutorService service = Executors.newFixedThreadPool(2); 10 // ExecutorService service = Executors.newSingleThreadExecutor(); 11 12 for (int i = 0; i < 5; i++) { 13 service.execute(new LiftOff()); 14 } 15 // shutDown方法,拒绝接受新的任务。我认为类似于io的close 16 service.shutdown(); 17 System.out.println("------------"); 18 } 19 }
2.3 从任务中产生返回值
Runnable只能执行任务,不能返回结果和异常。为了返回结果,可以实现Callable接口。
代码示例如:
1 import java.util.LinkedList; 2 import java.util.List; 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.Future; 8 9 public class ThreadDemo06 { 10 11 public static void main(String[] args) { 12 13 ExecutorService service = Executors.newCachedThreadPool(); 14 List<Future<String>> list = new LinkedList<Future<String>>(); 15 for (int i=0 ; i< 10 ; i++){ 16 list.add(service.submit(new TaskWithResult(i))); 17 } 18 for (Future<String> fs : list) { 19 try { 20 // get方法会或许返回值,如果任务尚未执行结束,会一直等待,造成阻塞 21 // 可以用isDone方法来判断,任务是否执行结束 22 System.out.println("future done? " + fs.isDone()); 23 System.out.println(fs.get()); 24 } catch (InterruptedException | ExecutionException e) { 25 e.printStackTrace(); 26 return; 27 } finally { 28 service.shutdown(); 29 } 30 } 31 } 32 } 33 34 class TaskWithResult implements Callable<String> { 35 36 private int id; 37 public TaskWithResult (int id) { 38 this.id = id; 39 } 40 @Override 41 public String call() throws Exception { 42 return "result of TaskWithResult " + id; 43 } 44 }
执行结果:
future done? true result of TaskWithResult 0 future done? true result of TaskWithResult 1 future done? true result of TaskWithResult 2 future done? true result of TaskWithResult 3 future done? true result of TaskWithResult 4 future done? true result of TaskWithResult 5 future done? true result of TaskWithResult 6 future done? true result of TaskWithResult 7 future done? true result of TaskWithResult 8 future done? true result of TaskWithResult 9
2.4 后台线程
所谓后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的后台线程结束时,程序也就终止了,同时杀死进程中的所有后台线程。(摘自《Java编程思想》第21章)
代码示例如下:
1 import java.util.concurrent.TimeUnit; 2 3 public class SimpleDaemons implements Runnable { 4 5 @Override 6 public void run() { 7 8 while (true) { 9 try { 10 TimeUnit.MICROSECONDS.sleep(100); 11 System.out.println(Thread.currentThread() + " " + this); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 System.out.println("sleep() intertupted"); 15 } 16 } 17 } 18 19 public static void main(String[] args) throws InterruptedException { 20 for (int i = 0; i < 10; i++) { 21 22 Thread daemon = new Thread(new SimpleDaemons()); 23 daemon.setDaemon(true); 24 daemon.start(); 25 } 26 System.out.println("All daemons started"); 27 TimeUnit.MICROSECONDS.sleep(175); 28 } 29 }