线程基础

1 初识线程

1.1 什么是线程

线程是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,共享内存资源。

1.2 线程与进程的区别

  1. 是否拥有资源:进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。
  2. 系统开销:创建、撤销及切换操作,线程的开销远小于进程。
  3. 通信方式:线程间通信可以直接访问进程中的同一内存空间,进程间通信需要借助 IPC

1.3 为什么需要线程

多线程,使得一个进程可以同时做好几件事。当进程中的某些线程阻塞时,另一些线程依旧可以执行,不需要把整个进程挂起。

另外,由于线程的开销比进程小很多(被称为“轻量级进程”),常使用多线程做并发编程(而不是多进程)。

实际运用举例:

  • 一个浏览器可以同时下载几幅图片;
  • 一个 Web 服务器可以同时服务并发的请求;
  • GUI 程序用一个独立的线程从宿主操作环境收集用户界面事件。

1.4 Java 中怎样创建一个线程

主要有两个基本方式:

  1. 继承 Thread 类
  2. 实现 Runnable 接口

第一种方式已经不推荐了,建议直接用第二种方式。为什么?因为实现 Runnable 接口的方式,可以将“并发任务”与“运行机制”解耦,更加灵活。可以选择为每个任务创建一个单独的线程,或是直接交给线程池。

以下是一个 demo:

public class RunnableTest {
    public static void main(String[] args) {
        Runnable r = () -> {
            for (int i = 0; i < 50; i++) {
                System.out.printf("%s %s\n", Thread.currentThread().getName(), i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
    }
}

2 扩展知识

2.1 线程状态

线程有 6 种状态:

  1. New(新建):线程刚创建,但还未运行(未调用 start 方法);
  2. Runnable(可运行):调用 start 方法之后。一个可运行的线程可能正在运行也可能没有运行;
  3. Blocked(阻塞):当一个线程试图获取一个内部的对象锁,而这个锁目前被其他线程占有,该线程就会被阻塞;
  4. Waiting(等待):当线程等待另一个线程通知调度器出现一个条件时,这个线程会进入等待状态。调用 Object.wait 方法或 Thread.join 方法,或者是等待 java.util.concurrent 库中的 Lock 或 Condition 时,就会出现这种情况;
  5. Timed waiting(计时等待):有几个方法有超时参数,调用这些方法会让线程进入计时等待状态。这一状态将一直保持到超时或接收到适当的通知。带有超时参数的方法有 Thread.sleep 和计时版的 Object.wait、Thread.join、Lock.tryLock 以及 Condition.wait;
  6. Terminated(终止):(1)run 方法正常退出,线程自然终止;(2)未捕获的异常终止了 run 方法,使线程意外终止。

当线程处阻塞或等待状态时,它暂时是不活动的。它不运行任何代码,而且消耗最少的资源。

状态转换图如下:

2.2 守护线程

可以调用 t.setDaemon(true),将一个线程转换为守护线程。

守护线程的唯一用途是为其他线程提供服务。当只剩下守护线程时,虚拟机就会退出。

举例:垃圾回收线程。

 


ref: