• [多线程] 新建线程以及线程的运行


    1、如何新建一个线程

    /**
     * 继承Thread类实现线程
     *
     */
    public class CreateByExtends extends Thread {
        @Override
        public void run() {
            System.out.println("Thread By Extends Thread");
            for (int i = 0; i < 10; i++) {
                System.out.println("Thread:" + i + "次");
            }
        }
    }
    /**
     * 通过实现Runnable接口实现线程
     * 
     * @author ss
     *
     */
    public class CreateByImplement implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread By implements Runnable");
            for (int i = 0; i < 10; i++) {
                System.out.println("Runnable:" + i + "次");
            }
        }
    }

    2、对线程进行调用

     1 public class Test01 {
     2     public static void main(String[] args) {
     3         /**
     4          * 通过继承Thread类的线程
     5          */
     6         Thread t1 = new CreateByExtends();
     7         
     8         /**
     9          * 通过实现Runnable接口的线程
    10          */
    11         Runnable r1=new CreateByImplement();
    12         /**
    13          * 两个线程随机获得时间片,顺序随机
    14          */
    15         t1.start();
    16         new Thread(r1).start();
    17         
    18     }
    19 }

    (1)通过start()方法进行线程调用,实现Runnable接口的线程,必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这根普通的方法调用没有任何区别。  事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的

    (2)线程在系统中通过获取时间片的方式运行,所以顺序是随机的

    输出的结果为:

    Thread By Extends Thread
    Thread By implements Runnable
    Thread:0次
    Runnable:0次
    Thread:1次
    Thread:2次
    Thread:3次
    Thread:4次
    Runnable:1次
    Thread:5次
    Runnable:2次
    Runnable:3次
    Runnable:4次
    Runnable:5次
    Runnable:6次
    Runnable:7次
    Runnable:8次
    Thread:6次
    Runnable:9次
    Thread:7次
    Thread:8次
    Thread:9次

    那如果我们直接调用线程中的run()方法呢:

     1 public class Test01 {
     2     public static void main(String[] args) {
     3         /**
     4          * 通过继承Thread类的线程
     5          */
     6         Thread t1 = new CreateByExtends();
     7         
     8         /**
     9          * 通过实现Runnable接口的线程
    10          */
    11         Runnable r1=new CreateByImplement();
    12 
    13         //如果调用线程的run方法
    14         t1.run();
    15         new Thread(r1).run();
    16     }
    17 }

    运行结果为:

    Thread By Extends Thread
    Thread:0次
    Thread:1次
    Thread:2次
    Thread:3次
    Thread:4次
    Thread:5次
    Thread:6次
    Thread:7次
    Thread:8次
    Thread:9次
    Thread By implements Runnable
    Runnable:0次
    Runnable:1次
    Runnable:2次
    Runnable:3次
    Runnable:4次
    Runnable:5次
    Runnable:6次
    Runnable:7次
    Runnable:8次
    Runnable:9次

    (1)这里线程运行的结果是按顺序的,所以我们可以得知,我们只是调用了run()方法而已,并不是两个线程。

    (2)通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别;

    (3)新线程创建的过程不会阻塞主线程的后续执行

    通过两种方式创建线程有什么区别呢?

    直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。

    其他实现线程的方法:

    使用ExecutorService、Callable、Future实现有返回结果的多线程

    可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。

    3、通过实现Callable接口,实现线程:

     1 /**
     2  * 通过实现Callable接口实现有返回结果的线程
     3  *
     4  */
     5 public class CreateByCallable implements Callable<Object>{
     6 
     7     @Override
     8     public Object call() throws Exception {
     9         System.out.println("Thread By implements Callable<V> ");
    10         for(int i=0;i<10;i++){
    11             System.out.println("Callabel<V>:"+i+"次");
    12         }
    13         return "call方法执行结束,返回这句话";
    14     }
    15 
    16 }

    调用线程:

     1 public class Test02 {
     2     public static void main(String[] args) {
     3 
     4         /**
     5          * 通过实现Callable<V>接口实现的线程
     6          */
     7         Callable<Object> c1 = new CreateByCallable();
     8 
     9         // 调用实现Callable接口的线程,需要使用FutureTask实现类的支持,用于接受结果
    10         FutureTask<Object> future = new FutureTask<>(c1);
    11 
    12         new Thread(future).start();
    13 
    14         // 接受线程运行后的结果
    15         try {
    16             Object result = future.get();// FutureTask可用于闭锁,类似于CountDownLatch的作用
    17             System.out.println(result.toString());
    18         } catch (InterruptedException | ExecutionException e) {
    19             e.printStackTrace();
    20         }
    21 
    22     }
    23 }

    结果:

    Thread By implements Callable<V> 
    Callabel<V>:0次
    Callabel<V>:1次
    Callabel<V>:2次
    Callabel<V>:3次
    Callabel<V>:4次
    Callabel<V>:5次
    Callabel<V>:6次
    Callabel<V>:7次
    Callabel<V>:8次
    Callabel<V>:9次
    call方法执行结束,返回这句话

    执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。

     1     public static void main(String[] args) {
     2         
     3         int taskSizes=5;
     4         //创建一个线程池
     5         ExecutorService pool=Executors.newFixedThreadPool(taskSizes);
     6         //创建有多个返回值的任务
     7         List<Future> list=new ArrayList<Future>();
     8         for(int i=0;i<taskSizes;i++){
     9             Callable c=new CreateByCallable();
    10             Future f=pool.submit(c);
    11             list.add(f);
    12         }
    13         //关闭线程池
    14         pool.shutdown();
    15         
    16         for(Future f:list){
    17             try {
    18                 System.out.println(f.get().toString());
    19             } catch (InterruptedException|ExecutionException e) {
    20                 e.printStackTrace();
    21             }
    22         }
    23     }

    运行结果:

    Thread By implements Callable<V> 
    Thread By implements Callable<V> 
    Thread By implements Callable<V> 
    Callabel<V>:0次
    Callabel<V>:0次
    Callabel<V>:0次
    Callabel<V>:1次
    Callabel<V>:2次
    Callabel<V>:3次
    Callabel<V>:4次
    Callabel<V>:1次
    Callabel<V>:5次
    Thread By implements Callable<V> 
    Callabel<V>:1次
    Callabel<V>:0次
    Callabel<V>:6次
    Callabel<V>:2次
    Callabel<V>:7次
    Callabel<V>:1次
    Callabel<V>:2次
    Callabel<V>:2次
    Callabel<V>:8次
    Callabel<V>:3次
    Callabel<V>:9次
    Callabel<V>:3次
    Callabel<V>:4次
    Callabel<V>:3次
    Callabel<V>:5次
    Callabel<V>:4次
    Callabel<V>:5次
    Callabel<V>:6次
    Callabel<V>:6次
    Callabel<V>:4次
    Callabel<V>:7次
    Callabel<V>:7次
    Callabel<V>:8次
    Callabel<V>:5次
    Callabel<V>:9次
    Callabel<V>:8次
    Callabel<V>:9次
    Callabel<V>:6次
    Callabel<V>:7次
    Callabel<V>:8次
    Callabel<V>:9次
    Thread By implements Callable<V> 
    call方法执行结束,返回这句话
    Callabel<V>:0次
    call方法执行结束,返回这句话
    call方法执行结束,返回这句话
    call方法执行结束,返回这句话
    Callabel<V>:1次
    Callabel<V>:2次
    Callabel<V>:3次
    Callabel<V>:4次
    Callabel<V>:5次
    Callabel<V>:6次
    Callabel<V>:7次
    Callabel<V>:8次
    Callabel<V>:9次
    call方法执行结束,返回这句话

    上述代码中Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

    public static ExecutorService newFixedThreadPool(int nThreads) 

    创建固定数目线程的线程池。

    public static ExecutorService newCachedThreadPool()
    创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

    public static ExecutorService newSingleThreadExecutor()
    创建一个单线程化的Executor。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

    ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

  • 相关阅读:
    我看Slashdot
    三维地形建模工具(MultiGenParadigm公司)
    街头新景:数字公交站
    美国一公司开发出WiFi定位系统 比GPS更精确
    理想、激情、生存—— 一位技术管理人员的20年工作经历和感悟 (ZT)
    西安国际化 市场化 人文化 生态化发展报告
    Google Earth Plus
    Visual Studio 2003 “默认设置”快捷键
    Google免费开放地图大餐
    向Windows 2000道声珍重
  • 原文地址:https://www.cnblogs.com/x-you/p/8604737.html
Copyright © 2020-2023  润新知