zoukankan      html  css  js  c++  java
  • 并发基础(Runnable、Thread、Executor)

    与顺序编程不同,并发使程序可以在“同一时间”执行多个操作。

         Java对并发编程提供了语言级别的支持。Java通过线程来实现并发程序。一个线程通常实现一个特定的任务,多个线程一起执行的时候就实现了并发。

         定义任务的最简单的方式就是实现Runnable接口。

    1 public interface Runnable {
    2     public abstract void run();
    3 }

         Runable只定义了一个run()方法。

         下面是一个监听用户输入的任务。

    复制代码
     1 public class Monitor implements Runnable {
     2 
     3     @Override
     4     public void run() {
     5         BufferedReader reader = new BufferedReader(new InputStreamReader(
     6                 System.in));
     7         while (true) {
     8             String str;
     9             try {
    10                 str = reader.readLine();
    11                 if (str.equals("quit")) {
    12                     return;
    13                 } else {
    14                     System.out.println(str);
    15                 }
    16             } catch (IOException e) {
    17                 e.printStackTrace();
    18             }
    19 
    20         }
    21     }
    22 }
    复制代码

         执行一个任务最简单的方式是把它交给一个Thread构造器。

    复制代码
    1 public class Test {
    2     public static void main(String[] args) {
    3         System.out.println("Main Start");
    4         Thread task = new Thread(new Monitor());
    5         task.start();
    6         System.out.println("Main End");
    7     }
    8 }
    复制代码

         执行上面的程序可以看到类似下面这样的结果:

         

         可以看到Main方法一次执行各语句到最后输出“Main End”,但Monitor依旧在运行,因为它在另一个线程中。

         除了Thread的方式,Java还提供了执行器Executor简化并发编程。

         Executor使用execute(Runnable command)方法执行一个任务,使用shutdown()方法防止新任务被提交给Executor,当前线程将继续执行shutdown()方法调用之前提交的任务。像这样:

    1 public static void main(String[] args) {
    2     System.out.println("Use Executor...");
    3     ExecutorService exec = Executors.newCachedThreadPool();
    4     exec.execute(new Monitor());
    5     exec.shutdown();
    6 }

         Executor的详细内容见《Java Executor框架分析》

         Runnable只是执行一个任务,但是并不能获取任务的执行结果(准确的说应该是run方法是一个void的方法,没有返回值)。如果希望获取任务的执行结果,那么可以选择实现Callable接口。

    1 public interface Callable<V> {
    2     V call() throws Exception;
    3 }

         它是一个接收泛型,且具有返回内容的“任务接口”。下面是一个通过Callable执行任务并获取返回结果的例子。

    复制代码
     1 public class Test {
     2     public static void main(String[] args) throws InterruptedException,
     3             ExecutionException {
     4         ExecutorService exec = Executors.newCachedThreadPool();
     5         List<Future<String>> results = new ArrayList<Future<String>>();
     6         for (int i = 0; i < 5; i++) {
     7             results.add(exec.submit(new TaskWithResult(i)));
     8         }
     9         exec.shutdown();
    10         for (Future<String> f : results) {
    11             System.out.println(f.get());
    12         }
    13     }
    14 }
    15 
    16 class TaskWithResult implements Callable<String> {
    17     private int id;
    18 
    19     public TaskWithResult(int id) {
    20         this.id = id;
    21     }
    22 
    23     @Override
    24     public String call() throws Exception {
    25         return "result of TaskWithResult#" + id;
    26     }
    27 }
    复制代码

    休眠

        Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。线程休眠是使线程让出CPU的最简单的做法之一。当线程休眠一定时间后,线程会苏醒,进入准备状态等待执行。

        Thread提供了两个sleep方法:Thread.sleep(long millis) 和Thread.sleep(long millis, int nanos)。

        线程可以通过休眠让出CPU的执行权限,但是这也是无法保证线程精确的执行次序的。下面是一个线程休眠的例子。

        枚举TimeUnit中也提供了sleep方法,可以通过它的实例去调用,如TimeUnit.MICROSECONDS.sleep(timeout)。

    sleep例子

    Thread run0
    Runnable run0
    Runnable run1
    Thread run1
    Thread run2
    Runnable run2
    Thread run3
    Runnable run3
    Runnable run4
    Thread run4   

        上面是运行结果。从运行结果中可以看出休眠可以让出执行权限,但是不能保证精确的执行顺序。
        枚举TimeUnit中也提供了sleep方法,可以通过它的实例去调用,如TimeUnit.MICROSECONDS.sleep(timeout)。

    让步

        如果已经完成了run()方法的循环和一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:工作已经做的差不多了,可以让别的线程占用CPU。这个暗示是通过调用yield()方法实现的。这只是一个暗示,未必会被采用。当调用yield()时,也是在建议具有相同优先级的线程可以运行。

        任何重要的调度都不能依赖于yield()。

     优先级

        线程的优先级将该线程的重要性传递给调度器。尽管CPU处理线程的顺序是不确定的,但是调度器将倾向于让优先级高的线程先执行。优先级较低的线程执行的频率较低。

        可以通过setPriority和getPriority来设置和获取线程的优先级。

        线程的优先级从1~10.超出这个值将报异常。

    join()

        Waits for this thread to die. 

        这是Thread中对join()方法的注释。

        线程可以在其他线程之上调用join()方法,效果等同于等待一段时间直到第二个线程结束才继续执行。如在t1中调用t2.join()则需要t2线程执行完后继续执行t1线程。

        join()方法还有其他形式,如带上超时时间,这样目前线程在这段时间内没有结束join方法也能返回。(其实join()方法调用的是join(0))

        join(long millis) 

        join(long millis, int nanos) 

        join()

    守护进程

        所谓守护线程,也可以叫后台线程,是指在程序运行的时候后台提供一种通用服务的线程,并且这种线程不属于程序中不可或缺的部分(只要有非后台线程还在运行,程序就不会终止。main就是一个非后台线程)。

    SimpleDaemons

    All daemons started
    Thread[Thread-8,5,main] com.skyjoo.test.SimpleDaemons@47b480
    Thread[Thread-1,5,main] com.skyjoo.test.SimpleDaemons@1bf216a
    Thread[Thread-2,5,main] com.skyjoo.test.SimpleDaemons@10d448
    Thread[Thread-3,5,main] com.skyjoo.test.SimpleDaemons@6ca1c
    Thread[Thread-6,5,main] com.skyjoo.test.SimpleDaemons@6ca1c
    Thread[Thread-9,5,main] com.skyjoo.test.SimpleDaemons@e0e1c6
    Thread[Thread-7,5,main] com.skyjoo.test.SimpleDaemons@19b49e6
    Thread[Thread-4,5,main] com.skyjoo.test.SimpleDaemons@47b480
    Thread[Thread-0,5,main] com.skyjoo.test.SimpleDaemons@156ee8e
    Thread[Thread-5,5,main] com.skyjoo.test.SimpleDaemons@156ee8e

        注意观察main中的sleep(175),如果设置成更长的时间将看到更多的输出结果,因为每个线程都在不断的输出结果。一旦main结束了,就没有非后台线程了,所以程序就终止了,所以就不会在有输出了。如果main中设置的sleep之间为0将看不到线程输出的结果,因为程序会马上结束掉。

        注意:可以通过isDaemon方法判断一个线程是否是后台线程。由后台线程创建的任何线程都将自动设置为后台线程。

      

     
     
    标签: 并发Thread
  • 相关阅读:
    深度优先搜索
    哈希算法
    双指针问题
    基本概念
    Ionic JPush极光推送二
    一条sql获取每个类别最新的一条记录
    Ionic App 更新插件cordova-plugin-app-version
    Ionic跳转到外网地址
    Ionic cordova-plugin-splashscreen
    Web API 上传下载文件
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2870554.html
Copyright © 2011-2022 走看看