文章目录

  • 进程( Process )
  • 线程(Thread)
  • 线程的三种创建方法
  • 一.继承Thread类创建线程类
  • 二.实现Runnable接口创建线程类
  • 三.使用Callable和Future创建线程(了解即可)


进程( Process )

  • 我们都知道程序是指令和数据的有序集合,它是一个静态的概念,而进程就是处于运行过程中的程序,它有自己的生命周期和各种不同的状态;并且它拥有自己独立的资源和私有的地址空间。进程是系统进行资源分配和调度的一个独立单位。
  • 当前的操作系统都支持同时运行多个任务,例如我们可以一边敲代码,一边听歌,一边回微信等等,每个独立运行的任务就是一个进程。这些进程看起来像是在同时工作,事实上并不是这样,对于一个CPU而言,它在某个时间点只能执行一个程序,也就是说只能运行一个进程,cpu不断在这些进程之间轮换执行,但是因为cpu的执行速度特别快,会给我们一种多个进程同时执行的错觉。这就是进程的并发性

线程(Thread)

  • 线程是进程的组成部分,一个进程可以拥有多个线程。
  • 线程是cpu调度和执行的单位。
  • 线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。
  • 线程是独立运行的,它并不知道进程中是否还有其他线程存在。线程的执行是抢占式的,当前运行的线程随时都可能被挂起,以便另外一个线程可以运行。线程的运行是由调度器控制的,人为不可干预。
  • 一个进程至少包含一个线程。在进程被初始化后,主线程(main())就被创建了,通常仅要求有一个主线程,作为系统的入口。除主线程,后台还有gc()线程等。

线程的三种创建方法

一.继承Thread类创建线程类
  1. 定义Thread的子类。
  2. 重写Thread的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
  3. 创建Thread子类的实例,即创建线程对象。
  4. 调用线程对象的start()方法来启动该线程
  5. 话不多说,上代码:
//通过继承Thread类来创建线程类
public class MyThread extends Thread {

    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            //当线程类继承Thread类时,使用This可获得当前线程
            //getName()是Thread类的实例方法,该方法返回调用该方法的线程名字
            //通过Thread对象的getName()方法返回当前线程的名字
            System.out.println(this.getName()+"   "+i);
        }
    }

    //主线程
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            //currentThread()是Thread类的静态方法,该方法总是返回当前正在执行的线程对象
            System.out.println(Thread.currentThread().getName()+"   "+i);

            if(i==7){
                //创建并启动一个线程
                new MyThread().start();
                //再次创建并启动一个线程
                new MyThread().start();
            }
        }
    }
}

//运行结果如下(每次的运行结果都有可能不一样,因为cpu安排调度,所以线程不一定立即执行)
main   0
main   1
main   2
main   3
main   4
main   5
main   6
main   7
main   8
main   9
main   10
main   11
main   12
main   13
Thread-1   0
Thread-1   1
Thread-1   2
Thread-1   3
Thread-1   4
Thread-1   5
Thread-1   6
Thread-1   7
Thread-1   8
Thread-1   9
Thread-1   10
Thread-1   11
Thread-1   12
Thread-0   0
Thread-0   1
Thread-0   2
Thread-0   3
Thread-0   4
Thread-0   5
Thread-0   6
Thread-0   7
Thread-0   8
Thread-1   13
main   14
Thread-1   14
Thread-1   15
Thread-1   16
Thread-1   17
Thread-1   18
Thread-1   19
Thread-0   9
Thread-0   10
Thread-0   11
Thread-0   12
Thread-0   13
Thread-0   14
Thread-0   15
Thread-0   16
Thread-0   17
Thread-0   18
Thread-0   19
main   15
main   16
main   17
main   18
main   19
二.实现Runnable接口创建线程类
  1. 定义Runnable接口的实现类MyRunnable。
  2. 实现run()方法,编写线程执行体
  3. 创建实现类MyRunnable的实例,将此实例作为Thread的target创建Thread对象,然后调用start()方法启动线程
  4. 话不多说,看代码
//通过实现Runnable接口来实现线程类
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //线程类实现Runnable接口时,若想获取当前线程,只能用Thread.currentThread().getName()
            System.out.println(Thread.currentThread().getName()+"   "+i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"   "+i);
            if(i==70){
                //创建实现类MyRunnable的实例
                MyRunnable myRunnable = new MyRunnable();
                //通过new Thread(target,name)方法创建新线程
                //将实例myRunnable作为Thread的target,然后调用start()方法启动线程
                new Thread(myRunnable,"线程一").start();
                new Thread(myRunnable,"线程二").start();
            }
        }
    }
}

//运行结果 略
三.使用Callable和Future创建线程(了解即可)
  1. Callable接口像是Runnable接口的增强版,Callable接口提供了一个call()方法来作为线程执行体,但call()方法比run()方法更强大。
  • call()方法可有有返回值
  • call()方法可以声明抛出异常
  1. 因此完全可以提供一个 Callable对象作为Thread的target,但是因为Callable接口不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target。
  2. java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该类实现了Future接口和Runnable接口,可以作为Thread类的Target。
  3. 代码如下(还有其他方法)
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class AnotherThread {
    public static void main(String[] args) {
        AnotherThread at = new AnotherThread();
        //使用Lambda表达式创建Callable<Integer>对象,并用FutureTask来包装此对象
        FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) () -> {
            int i = 0;
            for (; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "  " + i);
            }
            return i;
        });

        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "  " + i);
            if(i==70){
                new Thread(task).start();
            }
        }

    }
}
  • 前两种方式对比
  • 采用方式一:
  • 编写简单,但是继承了Thread,不能继承其他父类。不建议使用,避免oop单继承局限性
  • 采用方式二:
  • 编程稍复杂,但是避免了单继承局限性,多个线程可以共享一个target,方便多个线程来处理同一个对象,灵活方便。因而推荐使用第二种