美文网首页
基本的线程机制

基本的线程机制

作者: 魏杨 | 来源:发表于2018-07-23 09:37 被阅读0次

    [TOC]

    1. 定义任务 (run)

    //LiftOff.java
    //定义线程任务类
    public class LiftOff implements Runnable {
      protected int countDown = 10; // Default
      private static int taskCount = 0;
      private final int id = taskCount++;
      public LiftOff() {}
      public LiftOff(int countDown) {
        this.countDown = countDown;
      }
      public String status() {
        return "#" + id + "(" +
          (countDown > 0 ? countDown : "Liftoff!") + "), ";
      }
      public void run() {
        while(countDown-- > 0) {
          System.out.print(status());
          Thread.yield();// yield:让步,对线程调度器的一种建议:我已经执行完生命周期中最重要的部分,此刻正是切换线程的好时机。
        }
      }
    
    //BasicThreads.java
    //执行
    public class BasicThreads {
      public static void main(String[] args) {
        Thread t = new Thread(new LiftOff());
        t.start();
        System.out.println("Waiting for LiftOff");
      }
    } 
    
    /* Output: (90% match)
    Waiting for LiftOff
    #0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!),
    *///:~
    

    2. 使用Executor

    Java SE5 的 java.util.conrurrnt包中的执行器(Executor)将为你管理Thread对象,从而简化并发编程。

    ExcutorService:具有服务生命周期的Executor

    //LiftOff.java
    //定义线程任务类
    public class LiftOff implements Runnable {
      protected int countDown = 10; // Default
      private static int taskCount = 0;
      private final int id = taskCount++;
      public LiftOff() {}
      public LiftOff(int countDown) {
        this.countDown = countDown;
      }
      public String status() {
        return "#" + id + "(" +
          (countDown > 0 ? countDown : "Liftoff!") + "), ";
      }
      public void run() {
        while(countDown-- > 0) {
          System.out.print(status());
          Thread.yield();//yield:屈服,对线程调度器的一种建议:我已经执行完生命周期中最重要的部分,此刻正是切换线程的好时机。
        }
      }
    
    1. CachedThreadPool: 为每个任务创建一个线程
    // CachedThreadPool.java
    public class CachedThreadPool {
      public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++)
          exec.execute(new LiftOff());
        exec.shutdown();//防止新任务被提交给这个Executor
      }
    } /* Output: (Sample)
    #0(9), #0(8), #1(9), #2(9), #3(9), #4(9), #0(7), #1(8), #2(8), #3(8), #4(8), #0(6), #1(7), #2(7), #3(7), #4(7), #0(5), #1(6), #2(6), #3(6), #4(6), #0(4), #1(5), #2(5), #3(5), #4(5), #0(3), #1(4), #2(4), #3(4), #4(4), #0(2), #1(3), #2(3), #3(3), #4(3), #0(1), #1(2), #2(2), #3(2), #4(2), #0(Liftoff!), #1(1), #2(1), #3(1), #4(1), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!),
    *///:~
    
    1. FixedThreadPool: 可一次性预先执行代价高昂的线程分配,从而可以限制线程数量。
    // FixedThreadPool.java
    public class FixedThreadPool {
      public static void main(String[] args) {
        // Constructor argument is number of threads:
        ExecutorService exec = Executors.newFixedThreadPool(5);
        for(int i = 0; i < 5; i++)
          exec.execute(new LiftOff());
        exec.shutdown();
      }
    } /* Output: (Sample)
    #0(9), #0(8), #1(9), #2(9), #3(9), #4(9), #0(7), #1(8), #2(8), #3(8), #4(8), #0(6), #1(7), #2(7), #3(7), #4(7), #0(5), #1(6), #2(6), #3(6), #4(6), #0(4), #1(5), #2(5), #3(5), #4(5), #0(3), #1(4), #2(4), #3(4), #4(4), #0(2), #1(3), #2(3), #3(3), #4(3), #0(1), #1(2), #2(2), #3(2), #4(2), #0(Liftoff!), #1(1), #2(1), #3(1), #4(1), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!),
    *///:~
    
    1. SingleThreadExecutor: 线程数为1的FixedThreadPool。向SingleThreadExecutor提交多任务,这些任务会排队。
    // SingleThreadExecutor.java
    public class SingleThreadExecutor {
      public static void main(String[] args) {
        ExecutorService exec =
          Executors.newSingleThreadExecutor();
        for(int i = 0; i < 5; i++)
          exec.execute(new LiftOff());
        exec.shutdown();
      }
    } /* Output:
    #0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(Liftoff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(Liftoff!), #3(9), #3(8), #3(7), #3(6), #3(5), #3(4), #3(3), #3(2), #3(1), #3(Liftoff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(Liftoff!),
    *///:~
    

    3. 从任务中返回值 (call)

    Runnable是执行工作的独立任务,它无法返回值。此时,可以实现Callable接口,实现call(),并且必须使用ExecutorService.submit()调用它。

    // CallableDemo.java
    import java.util.concurrent.*;
    import java.util.*;
    
    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();
        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 {
            // get() blocks until completion:
            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
    *///:~
    

    submit() 会产生Future对象,它用Callable返回结果的特定类型进行参数化。可以使用isDone()来查询Future是否已完成。完成后可调用get()获取结果,否则,get()将阻塞,直到结果完成。

    4. 休眠 (sleep)

    //: SleepingTask.java
    // Calling sleep() to pause for a while.
    import java.util.concurrent.*;
    
    public class SleepingTask extends LiftOff {
      public void run() {
        try {
          while(countDown-- > 0) {
            System.out.print(status());
            // Old-style:
            // Thread.sleep(100);
            // Java SE5/6-style:
            TimeUnit.MILLISECONDS.sleep(100);
          }
        } catch(InterruptedException e) {
          System.err.println("Interrupted");
        }
      }
      public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i = 0; i < 5; i++)
          exec.execute(new SleepingTask());
        exec.shutdown();
      }
    } /* Output:
    #0(9), #1(9), #2(9), #3(9), #4(9), #0(8), #1(8), #2(8), #3(8), #4(8), #0(7), #1(7), #2(7), #3(7), #4(7), #0(6), #1(6), #2(6), #3(6), #4(6), #0(5), #1(5), #2(5), #3(5), #4(5), #0(4), #1(4), #2(4), #3(4), #4(4), #0(3), #1(3), #2(3), #3(3), #4(3), #0(2), #1(2), #2(2), #3(2), #4(2), #0(1), #1(1), #2(1), #3(1), #4(1), #0(Liftoff!), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!),
    *///:~
    

    调用sleep可抛出InterruptedException。异常在run()中被捕获,因为异常不能跨线程转播到main()中,所以必须本地处理。但也可以使用Excutor的异常捕获。

    5. 优先级 (priorities)

    线程优先级将该线程的重要性传递给了调度器,优先级仅仅是执行频率的高低。
    通常任务都有默认的优先级。

    你可以使用getPriority()读取优先级,并用setPriority()来修改他;

    //: SimplePriorities.java
    // Shows the use of thread priorities.
    import java.util.concurrent.*;
    
    public class SimplePriorities implements Runnable {
      private int countDown = 5;
      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) {
          // An expensive, interruptable operation:
          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: (70% match)
    Thread[pool-1-thread-6,10,main]: 5
    Thread[pool-1-thread-6,10,main]: 4
    Thread[pool-1-thread-6,10,main]: 3
    Thread[pool-1-thread-6,10,main]: 2
    Thread[pool-1-thread-6,10,main]: 1
    Thread[pool-1-thread-3,1,main]: 5
    Thread[pool-1-thread-2,1,main]: 5
    Thread[pool-1-thread-1,1,main]: 5
    Thread[pool-1-thread-5,1,main]: 5
    Thread[pool-1-thread-4,1,main]: 5
    ...
    *///:~
    

    尽管JDK有10个优先级,但它与大多数操作系统都不能映射好。通常只使用MIN_PRIORITY 、NORM_PRIORITY、 MAX_PRIORITY 三种级别。

    6. 让步 (yield)

    通过yield()方法暗示cpu,建议具有相同优先级的其他线程可以运行。

    7. 后台线程 (daemon)

    所谓后台线程,就是指在程序运行的时候在后台提供的一种通用的服务线程,且这种线程不属于程序中不可获取的部分。因此,所以的非后台线程结束时,程序就终结了,同时会杀死所以后台线程。反过来说,只要有任何非后台线程在运行,程序就不会终止。

    //: SimpleDaemons.java
    // Daemon threads don't prevent the program from ending.
    import java.util.concurrent.*;
    
    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());
          daemon.setDaemon(true); // Must call before start()
          daemon.start();
        }
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(175);
      }
    } /* Output: (Sample)
    All daemons started
    Thread[Thread-0,5,main] SimpleDaemons@530daa
    Thread[Thread-1,5,main] SimpleDaemons@a62fc3
    Thread[Thread-2,5,main] SimpleDaemons@89ae9e
    Thread[Thread-3,5,main] SimpleDaemons@1270b73
    Thread[Thread-4,5,main] SimpleDaemons@60aeb0
    Thread[Thread-5,5,main] SimpleDaemons@16caf43
    Thread[Thread-6,5,main] SimpleDaemons@66848c
    Thread[Thread-7,5,main] SimpleDaemons@8813f2
    Thread[Thread-8,5,main] SimpleDaemons@1d58aae
    Thread[Thread-9,5,main] SimpleDaemons@83cc67
    ...
    *///:~
    

    必须在线程启动之前调用setDaemon()方法,才能将它设置为后台线程。

    //: Daemons.java
    // Daemon threads spawn other daemon threads.
    import java.util.concurrent.*;
    
    class Daemon implements Runnable {
      private Thread[] t = new Thread[10];
      public void run() {
        for(int i = 0; i < t.length; i++) {
          t[i] = new Thread(new DaemonSpawn());
          t[i].start();
          System.out.println("DaemonSpawn " + i + " started, ");
        }
        for(int i = 0; i < t.length; i++)
          System.out.println("t[" + i + "].isDaemon() = " +
            t[i].isDaemon() + ", ");
        while(true)
          Thread.yield();
      }
    }
    
    class DaemonSpawn implements Runnable {
      public void run() {
        while(true)
          Thread.yield();
      }
    }
    
    public class Daemons {
      public static void main(String[] args) throws Exception {
        Thread d = new Thread(new Daemon());
        d.setDaemon(true);
        d.start();
        System.out.println("d.isDaemon() = " + d.isDaemon() + ", ");
        // Allow the daemon threads to
        // finish their startup processes:
        TimeUnit.SECONDS.sleep(1);
      }
    } /* Output: (Sample)
    d.isDaemon() = true, DaemonSpawn 0 started, DaemonSpawn 1 started, DaemonSpawn 2 started, DaemonSpawn 3 started, DaemonSpawn 4 started, DaemonSpawn 5 started, DaemonSpawn 6 started, DaemonSpawn 7 started, DaemonSpawn 8 started, DaemonSpawn 9 started, t[0].isDaemon() = true, t[1].isDaemon() = true, t[2].isDaemon() = true, t[3].isDaemon() = true, t[4].isDaemon() = true, t[5].isDaemon() = true, t[6].isDaemon() = true, t[7].isDaemon() = true, t[8].isDaemon() = true, t[9].isDaemon() = true,
    *///:~
    

    后台线程创建的线程都是后台线程。

    后台线程不会执行finally子句。因为该线程是随进程戛然而止的。

    8.隐式设置全局线程参数

    通过编写定制的ThreadFactory(实现接口ThreadFactory的newThread()方法)可以定制由Executor创建的线程的属性(后台,优先级,名称……)。

    //: DaemonFromFactory.java
    // Using a Thread Factory to create daemons.
    import java.util.concurrent.*;
    
    public class DaemonFromFactory implements Runnable {
      public void run() {
        try {
          while(true) {
            TimeUnit.MILLISECONDS.sleep(100);
            System.out.println(Thread.currentThread() + " " + this);
          }
        } catch(InterruptedException e) {
          System.out.println("Interrupted");
        }
      }
      public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool(
          new DaemonThreadFactory());
        for(int i = 0; i < 10; i++)
          exec.execute(new DaemonFromFactory());
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(500); // Run for a while
      }
    }
    
    class DaemonThreadFactory implements ThreadFactory {
      public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
      }
    }
    /* (Execute to see output) *///:~
    
    

    9. 编码的变体 (extends Thread)

    在简单情况下,直接从Thread继承

    //: SimpleThread.java
    // Inheriting directly from the Thread class.
    
    public class SimpleThread extends Thread {
      private int countDown = 5;
      private static int threadCount = 0;
      public SimpleThread() {
        // Store the thread name:
        super(Integer.toString(++threadCount));
        start();
      }
      public String toString() {
        return "#" + getName() + "(" + countDown + "), ";
      }
      public void run() {
        while(true) {
          System.out.print(this);
          if(--countDown == 0)
            return;
        }
      }
      public static void main(String[] args) {
        for(int i = 0; i < 5; i++)
          new SimpleThread();
      }
    } /* Output:
    #1(5), #1(4), #1(3), #1(2), #1(1), #2(5), #2(4), #2(3), #2(2), #2(1), #3(5), #3(4), #3(3), #3(2), #3(1), #4(5), #4(4), #4(3), #4(2), #4(1), #5(5), #5(4), #5(3), #5(2), #5(1),
    *///:~
    

    10. 加入一个线程 (join)

    一个线程可以在其他线程上调用join(),效果是等待一段时间直到第二个线程结束才执行。如果线程t2在线程t1上调用t1.join(),t2将被挂起,直到目标线程t1结束才恢复。

    也可以在join()中带上一个超时参数,如果目标线程t1在这段时间内未结束,join()总能返回。

    在调用线程t1上调用t1.interrupt() 中断 joint()。

    //: Joining.java
    // Understanding join().
    
    class Sleeper extends Thread {
      private int duration;
      public Sleeper(String name, int sleepTime) {
        super(name);
        duration = sleepTime;
        start();
      }
      public void run() {
        try {
          sleep(duration);
        } catch(InterruptedException e) {
          System.out.println(getName() + " was interrupted. " +
            "isInterrupted(): " + isInterrupted());
          return;
        }
        System.out.println(getName() + " has awakened");
      }
    }
    
    class Joiner extends Thread {
      private Sleeper sleeper;
      public Joiner(String name, Sleeper sleeper) {
        super(name);
        this.sleeper = sleeper;
        start();
      }
      public void run() {
       try {
          sleeper.join();
        } catch(InterruptedException e) {
         System.out.println("Interrupted");
        }
        System.out.println(getName() + " join completed");
      }
    }
    
    public class Joining {
      public static void main(String[] args) {
        Sleeper
          sleepy = new Sleeper("Sleepy", 1500),
          grumpy = new Sleeper("Grumpy", 1500);
        Joiner
          dopey = new Joiner("Dopey", sleepy),
          doc = new Joiner("Doc", grumpy);
        grumpy.interrupt();
      }
    } /* Output:
    Grumpy was interrupted. isInterrupted(): false
    Doc join completed
    Sleepy has awakened
    Dopey join completed
    *///:~
    

    11. 捕获异常 (CaptureUncaughtException)

    java SE5 后,可以用Executor捕获异常。为了捕获异常,我们修改Executor产生线程的方式。Thread.UncaughtExceptionHandler 接口允许你在每个Thread对象上附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。

    //: CaptureUncaughtException.java
    import java.util.concurrent.*;
    
    class ExceptionThread2 implements Runnable {
      public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run() by " + t);
        System.out.println(
          "eh = " + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
      }
    }
    
    class MyUncaughtExceptionHandler implements
    Thread.UncaughtExceptionHandler {
      public void uncaughtException(Thread t, Throwable e) {
        System.out.println("caught " + e);
      }
    }
    
    class HandlerThreadFactory implements ThreadFactory {
      public Thread newThread(Runnable r) {
        System.out.println(this + " creating new Thread");
        Thread t = new Thread(r);
        System.out.println("created " + t);
        t.setUncaughtExceptionHandler(
          new MyUncaughtExceptionHandler());
        System.out.println(
          "eh = " + t.getUncaughtExceptionHandler());
        return t;
      }
    }
    
    public class CaptureUncaughtException {
      public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool(
          new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());
      }
    } /* Output: (90% match)
    HandlerThreadFactory@de6ced creating new Thread
    created Thread[Thread-0,5,main]
    eh = MyUncaughtExceptionHandler@1fb8ee3
    run() by Thread[Thread-0,5,main]
    eh = MyUncaughtExceptionHandler@1fb8ee3
    caught java.lang.RuntimeException
    *///:~
    
    

    如果要在代码中处处使用相同的异常处理器,那就在Thread类中设置一个静态域,并把这个处理器设置为默认的未捕获异常处理器:

    //: SettingDefaultHandler.java
    import java.util.concurrent.*;
    
    public class SettingDefaultHandler {
      public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(
          new MyUncaughtExceptionHandler());
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
      }
    } /* Output:
    caught java.lang.RuntimeException
    *///:~
    

    相关文章

      网友评论

          本文标题:基本的线程机制

          本文链接:https://www.haomeiwen.com/subject/htedmftx.html