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

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会立即关闭所有的后台进程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值