线程基础——创建线程类

概述:

   进程(Process)是计算机中程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。通俗来说,我们的计算及在工作时会开启多个进程,例如我们可以一边用听歌软件听歌,一边用word编写文档,这就是两个独立的进程,这些进程可以是并发的,也就是说同时进行(实际上是由于这些进程交替运行的时间间隔短,我们很难察觉到非同时性)。
   线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。也就是说一个进程中可以包含多个线程,进程是线程的容器。
  本文主要总结建立线程的三种方式,其中后两种方式:实现Runnable接口与实现Callable接口属于同一种方式;直接继承Thread线程类是另外一种方式;它们的使用方式略有不同,注意区分。

1.直接继承Thread类

定义Thread类的子类,并重写run()方法。

public class FirstThread extends Thread {
    private int i=0;
    public void run() {
        for( ;i<5;i++) {
            System.out.println(this.getName()+"线程中的run方法执行"+" " +"i的值为:"+i);
        }
    }
    public static void main(String[] args) {
        for(int i=0;i<10;i++) {
            System.out.print("当前线程名字为:"+" ");
            System.out.println(Thread.currentThread().getName()+" " +"循环变量i的值为:"+i);
            if (i==5) {
                new FirstThread().start();
                new FirstThread().start();
            }
        }
    }

}

- 当前线程名字为: main 循环变量i的值为:0
- 当前线程名字为: main 循环变量i的值为:1
- 当前线程名字为: main 循环变量i的值为:2
- 当前线程名字为: main 循环变量i的值为:3
- 当前线程名字为: main 循环变量i的值为:4
- 当前线程名字为: main 循环变量i的值为:5

- Thread-0线程中的run方法执行 i的值为:0
- Thread-0线程中的run方法执行 i的值为:1
- Thread-0线程中的run方法执行 i的值为:2
- Thread-0线程中的run方法执行 i的值为:3
- Thread-0线程中的run方法执行 i的值为:4


- Thread-1线程中的run方法执行 i的值为:0
- Thread-1线程中的run方法执行 i的值为:1
- Thread-1线程中的run方法执行 i的值为:2
- Thread-1线程中的run方法执行 i的值为:3
- Thread-1线程中的run方法执行 i的值为:4

- 当前线程名字为: main 循环变量i的值为:6
- 当前线程名字为: main 循环变量i的值为:7
- 当前线程名字为: main 循环变量i的值为:8
- 当前线程名字为: main 循环变量i的值为:9

  FirstThread的在debug模式下逐步运行的结果如上所示,这可能更符合我们的认知,但是当我们直接run模式下运行程序时,程序的输出结果如下所示:

  • 当前线程名字为: main 循环变量i的值为:0
  • 当前线程名字为: main 循环变量i的值为:1
  • 当前线程名字为: main 循环变量i的值为:2
  • 当前线程名字为: main 循环变量i的值为:3
  • 当前线程名字为: main 循环变量i的值为:4
  • 当前线程名字为: main 循环变量i的值为:5
  • 当前线程名字为: main 循环变量i的值为:6
  • Thread-0线程中的run方法执行 i的值为:0
  • Thread-1线程中的run方法执行 i的值为:0
  • 当前线程名字为:
  • Thread-1线程中的run方法执行 i的值为:1
  • Thread-0线程中的run方法执行 i的值为:1
  • Thread-1线程中的run方法执行 i的值为:2
  • main 循环变量i的值为:7
  • 当前线程名字为:
  • Thread-1线程中的run方法执行 i的值为:3
  • Thread-0线程中的run方法执行 i的值为:2
  • Thread-1线程中的run方法执行 i的值为:4
  • main 循环变量i的值为:8
  • 当前线程名字为: main 循环变量i的值为:9
  • Thread-0线程中的run方法执行 i的值为:3
  • Thread-0线程中的run方法执行 i的值为:4

  **正如以上结果所示,在启动了子线程之后,程序中的三个线程(一个主线程与两个子线程)运行是毫无规律的,就算是主线程中相临的两天输出语句也会被子线程给阻塞,挂起。
  线程是独立运行的,程序启动之后,开启进程,一个进程中可能包含多个线程,线程运行时并不知道是否还有其他线程的执行,线程的执行是随机性的,或者更专业的说是抢占式的,当前运行的线程在任何时候都有可能被挂起,以便于另外一个线程的运行。

  在以上运行结果中,用不同颜色表示出了三个线程的相互阻塞情况,可以明显看到,一旦子线程被start(),那么三个线程将会是抢占式的运行。


  另外,需要注意在FirstThread类中定义了实例变量i,程序创建线程对象时都会创建一个FirstThread对象,两个子线程中并没有共用实例变量i,因为他们拥有各自的实例变量i。

**

2.实现Runnable接口创建线程类


  实现Runnable接口创建线程类时,不可直接使用该线程类开始线程,而是将该线程当作target传入到Thread的构造函数中创建Thread线程对象。

    public class SecondThread implements Runnable{
    private int i;
    @Override
    public void run() {
        for( ;i<5;i++) {
            //实现Runnable接口的线程类必须使用Thread类的静态方法获取当前线程
            System.out.println(Thread.currentThread().getName()+"线程中的run方法执行"+" " +"i的值为:"+i);
        }
    }
    public static void main(String[] args) {
        SecondThread st=new SecondThread();
        for(int i=0;i<10;i++) {
            System.out.print("当前线程名字为:"+" ");
            System.out.println(Thread.currentThread().getName()+" " +"循环变量i的值为:"+i);
            if (i==5) {
                new Thread(st,"子线程1").start();
                new Thread(st,"子线程2").start();
            }
        }
    }
}


  直接运行之后的结果如下所示:

  • 当前线程名字为: main 循环变量i的值为:0
  • 当前线程名字为: main 循环变量i的值为:1
  • 当前线程名字为: main 循环变量i的值为:2
  • 当前线程名字为: main 循环变量i的值为:3
  • 当前线程名字为: main 循环变量i的值为:4
  • 当前线程名字为: main 循环变量i的值为:5
  • 当前线程名字为: main 循环变量i的值为:6
  • 当前线程名字为: main 循环变量i的值为:7
  • 当前线程名字为: main 循环变量i的值为:8
  • 当前线程名字为: 子线程1线程中的run方法执行 i的值为:0
  • 子线程1线程中的run方法执行 i的值为:1
  • 子线程1线程中的run方法执行 i的值为:2
  • 子线程1线程中的run方法执行 i的值为:3
  • 子线程1线程中的run方法执行 i的值为:4

  • 子线程2线程中的run方法执行 i的值为:0
  • main 循环变量i的值为:9

  由输出结果可以看到,在子线程启动之后,三个线程抢占内存执行;另一方面,两个子线程中输出的实例变量i的值是连续的,这是因为,创建线程时是将自定义的线程类作为target传入到Thread类构造函数中创建线程对象,两个子线程共享该target实例,所以两个线程共享SecondThread的实例变量i,体现在输出结果中就是输出的值是连续的:
new Thread(st,"子线程1").start();

3.实现callable接口创建线程类

  Callable接口与Runnable接口类似,但Callable接口定义的call()方法作为线程的操作方法,该方法具有返回值,该返回值可以用Future接口表示,而FutureTask类实现了Future和Runnable接口,可以作为线程类Thread的target。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThirdThread implements Callable<Integer>{

    private int i;

    @Override
    public Integer call() throws Exception {
        for( ;i<5;i++) {
            //实现Runnable接口的线程类必须使用Thread类的静态方法获取当前线程
            System.out.println(Thread.currentThread().getName()+"线程中的run方法执行"+" " +"i的值为:"+i);
        }
        return i;
    }
    public static void main(String[] args) {
        ThirdThread tr=new ThirdThread();
        FutureTask<Integer> futureTask=new FutureTask<Integer>(tr);
        for(int i=0;i<10;i++) {
            System.out.print("当前线程名字为:"+" ");
            System.out.println(Thread.currentThread().getName()+" " +"循环变量i的值为:"+i);
            if (i==5) {
                new Thread(futureTask,"子线程1").start();
                new Thread(futureTask,"子线程2").start();
            }
        }
        /**
        *call()方法与run()方法类似,但是call()方法具有返回值 ,
        *利用FutureTask<>类的实例方法可以获取到该返回值
        */
        try {
            System.out.print("子线程运行的返回值:"+" "+futureTask.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


  Callable接口可以看做是Runnable接口的加强版,call()方法可以具有返回值,并且可以声明抛出异常,Future接口中有get()等方法来控制与它关联的Callable任务,在调用这些方法时需要显式抛出异常就是因为call()可以声明异常。
  程序输出结果如下所示:

  • 当前线程名字为: main 循环变量i的值为:0
  • 当前线程名字为: main 循环变量i的值为:1
  • 当前线程名字为: main 循环变量i的值为:2
  • 当前线程名字为: main 循环变量i的值为:3
  • 当前线程名字为: main 循环变量i的值为:4
  • 当前线程名字为: main 循环变量i的值为:5
  • 当前线程名字为: main 循环变量i的值为:6
  • 当前线程名字为: main 循环变量i的值为:7
  • 当前线程名字为: 子线程1线程中的call方法执行 i的值为:0
  • 子线程1线程中的call方法执行 i的值为:1
  • 子线程1线程中的call方法执行 i的值为:2
  • 子线程1线程中的call方法执行 i的值为:3
  • 子线程1线程中的call方法执行 i的值为:4
  • main 循环变量i的值为:8
  • 当前线程名字为: main 循环变量i的值为:9
  • 子线程运行的返回值: 5

总结

  从三种方式中可以看到,第一种方式是最简洁的,但是线程之间不能共享数据,而后两种方式都可以,实际应用中,使用实现Runnable接口的方式比较多,不同线程可以实现数据共享。

猜你喜欢

转载自blog.csdn.net/sandyxin5208/article/details/80245056