zoukankan      html  css  js  c++  java
  • 回炉再造-多线程的创建

     三种方式创建:  

      1. 通过继承Thread类来创建并启动多线程的方式

      2. 通过实现Runnable接口来创建并启动线程的方式

      3. 通过实现Callable接口来创建并启动线程的方式

      4. 总结Java中创建线程的方式,比较各自优势和区别

    一、继承Thread类创建线程类

      java使用Thread类表示线程,所有的线程对象都必须是Thread类或者其子类的实例,每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java中通过继承Thread类来创建并启动多线程的步骤如下:

      1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务

           因此把run()方法称为线程执行体

      2. 创建Thread子类的实例,即创建了线程对象

      3. 调用线程对象的start()方法来启动该线程

    代码实例:

    //继承Thread类创建线程
    public class ThreadCreat extends Thread{
    
        //线程执行方法
        @Override
        public void run() {
            for (int i = 0; i < 2; i++) {
           //获取当前线程名可以用this System.out.println(
    this.getName()); } } public static void main(String[] args) { for (int i = 0; i < 2; i++) { System.out.println("这里是主线程"+Thread.currentThread().getName()); if(i==1){ //创建一个线程 new ThreadCreat().start(); //创建第二个线程 new ThreadCreat().start(); } } } }

    运行结果:

      第一次运行结果:                                                                   第二次运行结果:

        这里是主线程main                  这里是主线程main

           这里是主线程main                  这里是主线程main

        Thread-0                      Thread-0
        Thread-1                      Thread-0
        Thread-1                      Thread-1
        Thread-0                      Thread-1

    该程序无论被执行多少次输出的记录数是一定的,一共是6条记录。主线程会执行for循环打印2条记录,两个子线程分别打印2条记录,一共6条记录。因为i变量是ThreadCreat的实例属性,而不是局部变量,但因为程序每次创建线程对象时都需要创建一个ThreadCreat对象,所以Thread-0和Thread-1不能共享该实例属性,所以每个线程都将执行2次循环。

    二、实现Runnable接口

      1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体

      2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象

      3. 调用线程对象的start()方法来启动线程

      Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法

    代码实例:   

    
    

    //实现Runnable接口创建线程
    public class ThreadCreat implements Runnable{

    
    

    private int i;
    void print(){
    System.out.println(Thread.currentThread().getName() + "" + i);
    }
    // run方法同样是线程执行体
    public void run() {
    for (; i < 2; i++) {
    // 当线程类实现Runnable接口时,
    // 如果想获取当前线程,只能用Thread.currentThread()方法。
    print();
    }
    }
    public static void main(String[] args) {
    for (int i = 0; i < 2; i++) {
    System.out.println(Thread.currentThread().getName() + "" + i);
    if (i == 1) {
    ThreadCreat st = new ThreadCreat();
    // 通过new Thread(target , name)方法创建新线程
    new Thread(st, "新线程-1").start();
    new Thread(st, "新线程-2").start();
    }
    }
    }
    }

     

      第一次执行结果:                                         

    main,0                                                                          main,0

    main,1                      main,1
    新线程-1,0                   新线程-1,0
    新线程-1,1                   新线程-1,1

    新线程-2,0

    从该运行结果中我们可以看出,控制台上输出的内容是乱序的,而且每次结果不尽相同。这是因为:
      1. 在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target。

      2. 所以多个线程可以共享同一个线程类即线程的target类的实例属性。

      3. 往控制台窗口print()输出的过程并不是多线程安全的,在一个线程输出过程中另一个线程也可以输出。

     

    实现Runnable接口比继承Thread类所具有的优势:

    1):适合多个相同的程序代码的线程去处理同一个资源

    2):可以避免java中的单继承的限制

    3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

    4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

    三、使用Callable和Future创建线程

    从Java 5开始,Java提供了Callable接口,该接口怎么看都像是Runnable接口的增强版,Callable接口提供了一个call()方法可以作为线程执行体,但call()方法比run()方法功能更强大

    call方法和run方法的区别:

      1. call()方法可以有返回值,run方法没有。

      2. call()方法可以声明抛出异常,run方法没有

    call()方法还有一 个返回值-----call()方法并不是直接调用,它是作为线程执行体被调用的。那么如何获取call()方法的返回值呢?

     Future接口概述

    Java 5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现了Future接口和Runnable接口可以作为Thread类的target。在Future接口里定义了如下几个公共方法来控制它关联的Callable任务:

      1. boolcan cancel(boolean maylnterruptltRunning):试图取消该Future里关联的Callable任务

      2. V get():返回Callable任务里call()方法的返回值。调用该方法将导致程序阻塞,必须等到子线程结束后才会得到返回值

      3. V get(long timeout,TimeUnit unit):返回Callable任务里call()方法的返回值。

        该方法让程序最多阻塞timeout和unit指定的时间,如果经过指定时间后Callable任务依然没有返回值,

        将会抛出TimeoutExccption异常

      4. boolean isCancelled():如果在Callable任务正常完成前被取消,则返回true

      5. boolean isDone():妇果Callable任务已完成,则返回true

    注意:Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同。

     创建并启动有返回值的线程的步骤

    1. 创建Callable接口的实现类,并实现call()方法,该cal()方法将作为线程执行体,且该call()方法有返回值

    2. 创建Callable实现类的实例,使用FutureTask类来包装Callable对象

          该FutureTask对象封装了该Callable对象的call()方法的返回值

    3. 使用FutureTask对象作为Thread对象的target创建并启动新线程

    4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

     代码实例:

    public class MyCallableTest implements Callable<Integer>{
        // 实现call方法,作为线程执行体
        public Integer call(){
            int i = 0;
            for ( ; i < 100 ; i++ ){
                System.out.println(Thread.currentThread().getName()+ "	" + i);
            }
            // call()方法可以有返回值
            return i;
        }
        public static void main(String[] args) {
            // 创建Callable对象
            MyCallableTest myCallableTest = new MyCallableTest();
            // 使用FutureTask来包装Callable对象
            FutureTask<Integer> task = new FutureTask<Integer>(myCallableTest);
            for (int i = 0 ; i < 100 ; i++){
                System.out.println(Thread.currentThread().getName()+ " 	" + i);
                if (i == 20){
                    // 实质还是以Callable对象来创建、并启动线程
                    new Thread(task , "callable").start();
                }
            }
            try{
                // 获取线程返回值
                System.out.println("callable返回值:" + task.get());
            }
            catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }

    运行上面程序,将看到主线程和call()方法所代表的线程交替执行的情形,程序最后还会输出call()方法的返回值。上面程序中创建Callable实现类与创建Runnable实现类并没有太大的差别,只是Callable的call()方法允许声明抛出异常, 而且允许带返回值。当主线程中当循环变量i等于20时,程序启动以FutureTask对象为target的线程。程序最后调用FutureTask对象 的get()方法来返回call()方法的返回值——该方法将导致主线程阻塞,直到call()方法结束并返回为止。

     原文链接http://www.cnblogs.com/sunddenly/p/4399061.html

  • 相关阅读:
    Qt之JSON生成与解析
    Qt的QLineEdit显示密码
    Ubuntu14.04 64位运行32位程序
    Xcode 7在支持ipad的设备中需要支持分屏!
    「模板」平衡树
    [NOI2016]区间 题解(决策单调性+线段树优化)
    [NOIP模拟测试12]题解
    [笔记乱写]0/1分数规划
    [NOIP模拟测试11] 题解
    [SCOI2014]方伯伯的玉米田 题解(树状数组优化dp)
  • 原文地址:https://www.cnblogs.com/xing-12/p/9048109.html
Copyright © 2011-2022 走看看