• Java多线程学习(二)


    一、定义产生返回值的任务

          在上一篇文的介绍中,我们知道了定义任务通常的方法是定义一个实现Runnable接口的类,这个类被我们成为任务。然而也很容易注意到,任务的最重要的一个方法就是run( )方法,而run( )方法是没有返回值的,也就是说我们之前定义的任务不返回任何值。

          如果想要定义一个有返回值的任务,则需要编写一个实现Callable接口的类。Callable是一种具有类型参数的泛型,他的类型参数表示的是call( )方法的返回值类型。

    示例如下:

     1 import java.util.concurrent.Callable;
     2 import java.util.concurrent.ExecutionException;
     3 import java.util.concurrent.ExecutorService;
     4 import java.util.concurrent.Executors;
     5 import java.util.concurrent.Future;
     6 
     7 /**
     8  * @author Gao
     9  * Date:2016.5
    10  * */
    11 
    12 class Task implements Callable<String> {
    13     int testNum;
    14     public Task(int testNum) {
    15         this.testNum = testNum;
    16     }
    17     public String call() {
    18         return "Test number is " + testNum;
    19     }
    20 }
    21 
    22 public class ReturnedDemo {
    23     public static void main(String[] args) {
    24         //创建ExecutorService的实例化对象,用来执行任务。
    25         ExecutorService exec = Executors.newCachedThreadPool();
    26         Future<String> future = exec.submit(new Task(123));
    27         try {
    28             
    29             System.out.println(future.get());
    30         } catch (InterruptedException e) {
    31             // TODO 自动生成的 catch 块
    32             e.printStackTrace();
    33         } catch (ExecutionException e) {
    34             // TODO 自动生成的 catch 块
    35             e.printStackTrace();
    36         }
    37         
    38     }
    39 }
    40 /*Output:
    41 Test number is 123
    42 */

          在上面的示例代码中你可能会不了解Future<T>的含义。ExecutorService类的实例化对象在调用submit( )方法时会返回Future<T>类型的返回值,我们在这里实例化的future对象用来保存该返回值,在之后用来处理和其他过程。同时,在调用get( )方法时会产生异常,所以该段用try-catch块进行包围。

    二、线程的优先级与让步

    1.线程的优先级

          线程由线程调度器调用执行,线程调度器进行线程调度的时候倾向于让优先级高的线程先运行。但是由于CPU处理线程集的顺序是不固定的,因此线程调度器只是起到一个“建议”的作用,而不是决定性作用。同时,线程优先级只是代表线程集中的线程执行的频率。高优先级的线程执行的频率较高,但是低优先级的线程也会得到执行。

          由于不同操作系统的优先级不同且和JDK的优先级不同(JDK有10个优先级),所以JDK和操作系统优先级的映射不是很好。通常我们在调整优先级的时候,仅仅使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三种级别。

    示例代码如下:

     1 import java.util.concurrent.ExecutorService;
     2 import java.util.concurrent.Executors;
     3 
     4 /**
     5  * @author Gao
     6  * Date:2016.5
     7  * */
     8 class PriorityTask implements Runnable {
     9     int priority;
    10     public PriorityTask(int priority) {
    11         this.priority = priority;
    12     }
    13     @Override
    14     public void run() {
    15         Thread.currentThread().setPriority(priority);
    16         System.out.println(this);
    17     }
    18     public String toString() {
    19         return Thread.currentThread() + ";";
    20     }
    21 }
    22 public class PriorityTest {
    23     public static void main(String[] args) {
    24         ExecutorService exec = Executors.newCachedThreadPool();
    25         for(int i = 0; i < 5; i++) 
    26             exec.execute(new PriorityTask(Thread.MAX_PRIORITY));
    27         exec.execute(new PriorityTask(Thread.MIN_PRIORITY));
    28         exec.shutdown();
    29         
    30     }
    31 }    
    32 /*Output:
    33 Thread[pool-1-thread-3,10,main];
    34 Thread[pool-1-thread-4,10,main];
    35 Thread[pool-1-thread-1,10,main];
    36 Thread[pool-1-thread-5,10,main];
    37 Thread[pool-1-thread-2,10,main];
    38 Thread[pool-1-thread-6,1,main];
    39 
    40 */

           在该示例中,我们通过给线程设置不同的优先级来定义线程,在线程的信息中会显示其优先级。

    2.线程的让步

         在第一篇文章的介绍中,我们在例子中应用了Thread.yield( )这一方法,这个方法表现的就是线程的让步。通常我们在run( )方法的其他方法体执行完成之后执行这一方法,意在给线程调度器发送一个信号,告诉线程调度器自己的工作已经基本执行完毕,可以将CPU分配给其他线程进行使用了。但是和上面的线程优先级中的介绍一样,由于CPU处理现有线程集的顺序是不确定的,所以yield( )方法只是给调度器一个建议,并不是真正的在执行yield( )方法后就将CPU分给其他同级别需要CPU的线程。

    3.后台线程

    (1)后台线程的定义:后台线程是指在程序运行的时候在后台提供一种通用服务的线程,

    这种线程一般不属于程序不可缺少的部分。

    (2)后台线程的设置:利用线程对象调用setDaemon( )方法,可将其设置为后台线程。

    注:必须在线程启动之前设置,才能成功将其设置为后台线程。

    三、与线程有关的异常问题

          提起异常,想必大家都不会感到陌生。对于多线程编程中来说,如果你不采用一些步骤来捕获这些异常,则一旦异常逃出任务的run( )方法,它就会向外传播到控制台。

    目前为了解决线程异常捕获的问题,我们可以通过修改Executor产生线程的方式。实现的基础是Java提供的接口:Thread.UncaughtExceptionHandler。该接口允许程序员在编写的每个Thread对象上都附着一个异常处理器。

    ps:有关于异常的例子会在下一篇博文中介绍并解释。

  • 相关阅读:
    LoadRunner12 Java Vuser API语法举例
    Java代码封装redis工具类
    Java代码redis基础操作
    Git提交代码失败: empty ident name (for <>) not allowed
    Ubuntu 16.04 root环境变量不生效问题解决方案
    Jenkins中使用GitLab的配置
    gitlab搭建与配置说明
    移动端网页开发经验总结 (不断更新ing)
    移动端开发注意事项(转载)
    电脑上调试手机网站的几种方法
  • 原文地址:https://www.cnblogs.com/JaydenRansom/p/5491342.html
Copyright © 2020-2023  润新知