一. 简介

1.1 线程与进程的概念

进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。

多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。采用多线程的编程方式,能充分利用 CPU 资源,显著的提升程序的执行效率。

二. 多线程的实现

2.1. 继承thread

通过继承Thread类来创建线程是最简单的一种方法,继承类重写run()方法,然后通过线程对象实例去调用start()方法即可启动线程。

public class ThreadTest extends Thread {
    private String msg;

    public ThreadTest (String msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(msg + "运行  :  " + i);
            try {
                sleep((int) (Math.random() * 5));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadTest t1 = new ThreadTest("zhangsan");
        ThreadTest t2 = new ThreadTest("sunny");
        t1.start();
        t2.start();
    }
}

运行结果为:
zhangsan运行 : 0
sunny运行 : 0
sunny运行 : 1
sunny运行 : 2
zhangsan运行 : 1
zhangsan运行 : 2
zhangsan运行 : 3
sunny运行 : 3
zhangsan运行 : 4
sunny运行 : 4

注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的;多线程程序是乱序执行。多个线程分别完成自己的任务。

2.2 实现Runnable

多个线程共同完成一个任务;不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。
开发者只需要实现Runnable接口,然后通过一个Thread类来启动。

public class MyThread implements Runnable{    
    @Override    
    public void run() {        
        System.out.println(Thread.currentThread().getName() + "在运行!");    
     }
}

Thread thread = new Thread(new MyThread());
thread.start();

2.3 实现Callable

使用Callable+Future获取执行结果,并且可以抛出异常。

public class CallableDemo implements Callable<Integer> {

@Override
public Integer call() throws Exception {
    int sum = 0;
    System.out.println("Callable子线程开始计算啦!" + Thread.currentThread().getName());
    Thread.sleep(2000);

    for(int i=0 ;i<5000;i++){
        sum=sum+i;
    }
    System.out.println("Callable子线程计算结束!" + Thread.currentThread().getName());
    return sum;
}

}

Callable执行测试类如下:

public class CallableTest {  
    public static void main(String[] args) {  
        //创建线程池  
        ExecutorService es = Executors.newSingleThreadExecutor();
        //创建Callable对象任务  
        CallableDemo calTask = new CallableDemo();
        //提交任务并获取执行结果  
        Future<Integer> future = es.submit(calTask);
        //关闭线程池  
        es.shutdown();
        try {
            Thread.sleep(2000);
            System.out.println("主线程在执行其他任务");
    
            if (future.get() != null) {
                //输出获取到的结果  
                System.out.println("future.get()-->" + future.get());
            } else {

                //输出获取到的结果  
                System.out.println("future.get()未获取到结果");
            }
    
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主线程在执行完成");  
    }  
}

2.4 使用 JDK 8 的 Lambda 创建线程

Lambda 表达式,是从 JDK1.8 版本开始加入的,可以看作成通过实现Runnable接口创建线程的一种简写。

new Thread(()-> System.out.println(Thread.currentThread().getName() + "在运行!")).start();

2.5 总结

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

  1. 适合多个相同的程序代码的线程去处理同一个资源
  2. 可以避免java中的单继承的限制
  3. 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
  4. 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类。

需要特别注意的地方是:真正启动线程的是start()方法而不是run()方法,单独调用run()方法和调用普通的成员方法一样,不能启动线程。


jacheut
4 声望1 粉丝