Java编程思想 之 线程简单应用

来源:互联网 发布:个人顶级域名 编辑:程序博客网 时间:2024/05/21 06:54

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");  }}
//outputWaiting 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();      }  }}
//outputresult of TaskWithResult 0result of TaskWithResult 1result of TaskWithResult 2result of TaskWithResult 3result of TaskWithResult 4result of TaskWithResult 5result of TaskWithResult 6result of TaskWithResult 7result of TaskWithResult 8result 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();  }}
//outputThread[pool-1-thread-3,1,main]: 5Thread[pool-1-thread-6,10,main]: 5Thread[pool-1-thread-2,1,main]: 5Thread[pool-1-thread-3,1,main]: 4Thread[pool-1-thread-1,1,main]: 5Thread[pool-1-thread-4,1,main]: 5Thread[pool-1-thread-5,1,main]: 5Thread[pool-1-thread-6,10,main]: 4Thread[pool-1-thread-3,1,main]: 3Thread[pool-1-thread-6,10,main]: 3Thread[pool-1-thread-2,1,main]: 4Thread[pool-1-thread-3,1,main]: 2Thread[pool-1-thread-1,1,main]: 4Thread[pool-1-thread-6,10,main]: 2Thread[pool-1-thread-5,1,main]: 4Thread[pool-1-thread-4,1,main]: 4Thread[pool-1-thread-3,1,main]: 1Thread[pool-1-thread-6,10,main]: 1Thread[pool-1-thread-2,1,main]: 3Thread[pool-1-thread-4,1,main]: 3Thread[pool-1-thread-5,1,main]: 3Thread[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);  }}
//outputAll daemons startedThread[Thread-1,5,main] com.praysz.thread.SimpleDaemons@126be4ccThread[Thread-8,5,main] com.praysz.thread.SimpleDaemons@4e5ced83Thread[Thread-7,5,main] com.praysz.thread.SimpleDaemons@36ed5ba6Thread[Thread-2,5,main] com.praysz.thread.SimpleDaemons@697a1686Thread[Thread-4,5,main] com.praysz.thread.SimpleDaemons@697a1686Thread[Thread-0,5,main] com.praysz.thread.SimpleDaemons@126be4ccThread[Thread-6,5,main] ......

JavaSE5引入了更加显示的sleep版本,作为TimeUnit类的一部分.
当把main方法中的最后TimeUnit.MILLISECONDS.sleep(175)去除后

//outputAll daemons started

这说明,当最后一个非后台线程终止时,后台线程会”突然终止”,因此一旦main()方法退出,JVM会立即关闭所有的后台进程.

0 0
原创粉丝点击