线程介绍

线程(Thread)是程序运行的执行单元,依托于进程存在。一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源,因此线程之间的切换更加节省资源、更加轻量化,因而也被称为轻量级的进程。

什么是进程

进程(Processes)是程序的一次动态执行,是系统进行资源分配和调度的基本单位,是操作系统运行的基础,通常每一个进程都拥有自己独立的内存空间和系统资源。简单来说,进程可以被当做是一个正在运行的程序。

为什么需要线程

程序的运行必须依靠进程,进程的实际执行单元就是线程。

为什么需要多线程

多线程可以提高程序的执行性能。例如,有个 90 平方的房子,一个人打扫需要花费 30 分钟,三个人打扫就只需要 10 分钟,这三个人就是程序中的“多线程”。

java 创建多线程的三种方式

  • 继承 Thread 类,重写 run 方法
  • 实现 Runnable 接口,实现 run 方法
  • 实现 Callable 接口,实现 call 方法

下面分别来看看线程创建和使用的具体代码。

1)继承 Thread 类
请参考以下代码:

class ThreadTest {
    public static void main(String[] args) throws Exception {
        MyThread thread = new MyThread();
        thread.start();
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread");
    }
}

程序执行结果:
Thread

2)实现 Runnable 接口
请参考以下代码:

class ThreadTest {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable).start();
    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable");
    }
}

程序执行结果:

Runnable

3)实现 Callable 接口

class ThreadTest {
    public static void main(String[] args) throws Exception {
        MyCallable callable = new MyCallable();
        // 定义返回结果
        FutureTask<String> result = new FutureTask(callable);
        // 执行程序
        new Thread(result).start();
        // 输出返回结果
        System.out.println(result.get());
    }
}
class MyCallable implements Callable {
    @Override
    public String call() {
        System.out.println("Callable");
        return "Success";
    }
}

以上程序执行结果如下:

Callable
Success

可以看出,Callable 的调用是可以有返回值的,它弥补了之前调用线程没有返回值的情况,它是随着 JDK 1.5 一起发布的。

4)JDK 8 创建线程
JDK 8 之后可以使用 Lambda 表达式很方便地创建线程,请参考以下代码:

new Thread(() -> System.out.println("Lambda Of Thread.")).start();

线程高级用法

线程等待

使用 wait() 方法实现线程等待,代码如下:

System.out.println(LocalDateTime.now());
Object lock = new Object();
Thread thread = new Thread(() -> {
    synchronized (lock){
        try {
            // 1 秒钟之后自动唤醒
            lock.wait(1000);
            System.out.println(LocalDateTime.now());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
thread.start();

以上程序执行结果如下:
2019-06-22T20:53:08.776
2019-06-22T20:53:09.788

注意:当使用 wait() 方法时,必须先持有当前对象的锁,否则会抛出异常 java.lang.IllegalMonitorStateException。

线程唤醒

使用 notify()/notifyAll() 方法唤醒线程。

  • notify() 方法随机唤醒对象的等待池中的一个线程;
  • notifyAll() 唤醒对象的等待池中的所有线程。

使用如下:

Object lock = new Object();
lock.wait();
lock.notify();
// lock.notifyAll();

线程休眠

// 休眠 1 秒
Thread.sleep(1000);

等待线程执行完成

Thread joinThread = new Thread(() -> {
    try {
        System.out.println("执行前");
        Thread.sleep(1000);
        System.out.println("执行后");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
joinThread.start();
joinThread.join();
System.out.println("主程序");

以上程序执行结果:

执行前

执行后

主程序

yield 交出 CPU 执行权

new Thread(){
    @Override
    public void run() {
        for (int i = 1; i < 10; i++) {
            if (i == 5) {
                // 让同优先级的线程有执行的机会
                this.yield();
            }
        }
    }
}.start();

注意:yield 方法是让同优先级的线程有执行的机会,但不能保证自己会从正在运行的状态迅速转换到可运行的状态。

线程中断

使用 System.exit(0) 可以让整个程序退出;要中断单个线程,可配合 interrupt() 对线程进行“中断”。
使用代码如下:

Thread interruptThread = new Thread() {
    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            System.out.println("i:" + i);
            if (this.isInterrupted()) {
                break;
            }
        }
    }
};
interruptThread.start();
Thread.sleep(10);
interruptThread.interrupt();

线程优先级

在 Java 语言中,每一个线程有一个优先级,默认情况下,一个线程继承它父类的优先级。可以使用 setPriority 方法设置(1-10)优先级,默认的优先级是 5,数字越大表示优先级越高,优先级越高的线程可能优先被执行的概率就越大。
设置优先级的代码如下:

Thread thread = new Thread(() -> System.out.println("Java"));
thread.setPriority(10);
thread.start();

死锁

死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
比如,当线程 A 持有独占锁 a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 A B 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
死锁示意图如下所示:

deadlock.jpg

死锁代码:

Object obj1 = new Object();
Object obj2 = new Object();
// 线程1拥有对象1,想要等待获取对象2
new Thread() {
    @Override
    public void run() {
        synchronized (obj1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj2) {
                System.out.println(Thread.currentThread().getName());
            }
        }
    }
}.start();
// 线程2拥有对象2,想要等待获取对象1
new Thread() {
    @Override
    public void run() {
        synchronized (obj2) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj1) {
                System.out.println(Thread.currentThread().getName());
            }
        }
    }
}.start();
文章目录