在编程中,多线程的意思是某个程序同时多个任务,这样的每一个任务则称为一个线程。线程这部分涉及的知识非常多,在实际开发的应用也是非常重要,这里将介绍基本的部分。

概念

线程:操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中实际运作单位。
进程:计算机中已运行程序的实体。进程本身不会运行,是线程的容器。
并行与并发:

  • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
  • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。[1]

实现多线程的几种基本方式

1、继承Thread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test02 extends Thread{

private String name;

public Test02(String name) {
this.name = name;
}

@Override
public void run() {
for(int i=0; i<100; i++){
System.out.println(name + i);
}
}

public static void main(String[] args) throws InterruptedException {
Test02 test1 = new Test02("one-->");
Test02 test2 = new Test02("two-->");
test1.start();
test2.start();
}
}

2、实现Runnable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test01 implements Runnable{

private String name;

public Test01(String name){
this.name = name;
}

@Override
public void run() {
for(int i=0; i<100; i++){
System.out.println(name+"--->"+i);
}
}

public static void main(String[] args) {
Test01 test1 = new Test01("one");
Test01 test2 = new Test01("two");

Thread thread1 = new Thread(test1);//使用thread类执行start方法
Thread thread2 = new Thread(test2);
thread1.start();
thread2.start();
}
}

3、使用Callable和Future接口创建线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test03 implements Callable<Integer>{

@Override
public Integer call() throws Exception {
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
return null;
}

public static void main(String[] args) {

Test03 t1 = new Test03();
Test03 t2 = new Test03();
FutureTask<Integer> task1 = new FutureTask<>(t1);
FutureTask<Integer> task2 = new FutureTask<>(t2);
new Thread(task1, "新建线程1").start();
new Thread(task2, "新建线程2").start();
}

}

线程状态

使用getState()方法可获取当前线程的状态(枚举类型),各种状态如下:

  • New(新生)
    当new一个新的线程时,线程还没开始运行时,状态是new

    1
    2
    3
    4
    5
    6
    public class Test {
    public static void main(String[] args) {
    Thread thread = new Thread();
    System.out.println(thread.getState());
    }
    }

    输出
    NEW

  • Runnable(可运行)
    线程调用start执行时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Test {
    public static void main(String[] args) {
    Thread thread = new Thread(new Runnable() {
    public void run() {
    for(int i=0; i<100; i++){
    System.out.println("i is "+i);
    }
    }
    });
    thread.start();
    System.out.println(thread.getState());
    }
    }

    输出
    RUNNABLE
    i is 0
    i is 1
    ...
    i is 99

  • Blocked(被阻塞)
    当一个线程试图获取一个内部的对象锁(而不是java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Test {
    final Object lock = new Object();
    class Example implements Runnable{
    private String name;
    public Example(String name) {
    this.name = name;
    }
    @Override
    public void run() {
    //同步代码块
    synchronized (lock) {
    for(int i=0; i<100; i++){
    System.out.println(name+": i is "+i);
    }
    }
    }
    }

    public static void main(String[] args) {
    Example example1 = new Test().new Example("test1");
    Example example2 = new Test().new Example("test2");
    Thread thread1 = new Thread(example1);
    Thread thread2 = new Thread(example2);
    thread1.start();
    thread2.start();
    System.out.println(thread1.getState());
    System.out.println(thread2.getState());
    }
    }

    输出
    RUNNABLE
    test1: i is 0
    test2: i is 0
    test1: i is 1
    BLOCKED
    test1: i is 2
    ...

  • Waiting(等待)
    等待状态,释放自身的锁进入Waiting状态并加入线程等待队列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Test {

    public synchronized void testMethod(){
    try {
    this.wait();
    System.out.println("waiting closed.");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread(new Runnable() {
    public void run() {
    new Test().testMethod();
    }
    });
    thread1.start();
    Thread.sleep(10);//主线程休眠,转而去执行子进程
    System.out.println(thread1.getState());
    }
    }

    输出
    WAITING
    waiting closed并不会输出

  • Timed waiting(计时等待)
    this.wait();加上等待时间就会进入计时等待状态,例如this.wait(3000);等待三秒输出内容
    TIMED_WAITING
    waiting closed.

  • Terminated(被终止)
    run方法执行结束线程终止

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Test {
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(new Runnable() {
    public void run() {
    System.out.println("run success");
    }
    });
    thread.start();
    Thread.sleep(100);
    System.out.println(thread.getState());
    }
    }

线程管理

  1. sleep方法
    static void sleep(long millis)
    让当前正在运行的线程休眠一段时间
    sleep是一个静态方法,不要用实例化的线程对象调用,其作用的是当前正在运行的线程。
    Thread.sleep(2000);表示让当前线程休眠2秒,这里的2秒并不是准确的时间段,因为线程是由系统控制,实际时间可能大于2秒。

  2. yield方法
    static void yield()
    暂停当前正在执行的线程,重新进入就绪状态,这也是和sleep方法的区别的地方。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。

  3. join方法
    合并线程。等待终止指定的线程,让主线程等待子线程结束之后再执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class Test {
    public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread(new Runnable() {
    public void run() {
    for(int i=0; i<100; i++){
    System.out.println("one--"+i);
    }
    }
    });
    Thread thread2 = new Thread(new Runnable() {
    public void run() {
    thread1.start();
    try {
    thread1.join();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    for(int i=0; i<100; i++){
    System.out.println("two--"+i);
    }
    }
    });
    thread2.start();
    }
    }

    thread1在thread2中启动,为thread2子线程,调用join,等待thread1执行结束再执行thread2,相当于把两线程合并了。
    void join(long millis)
    join重载方法带参数,在指定时间段子线程未执行完将重新进入就绪状态,等待cpu调度。

  4. notify和notifyAll方法
    notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。
    notifyAll 会唤醒所有等待(对象的)线程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Test {
    public synchronized void testMethod(){
    try {
    System.out.println("thread start.");
    this.wait();
    System.out.println("waiting closed.");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    public synchronized void notifyMethod(){
    this.notifyAll();
    }

    public static void main(String[] args) throws InterruptedException {
    Test test = new Test();
    Thread thread1 = new Thread(new Runnable() {
    public void run() {
    test.testMethod();
    }
    });

    Thread thread2 = new Thread(new Runnable() {
    public void run() {
    test.notifyMethod();
    System.out.println("唤醒线程.....");
    }
    });
    thread1.start();
    Thread.sleep(1000);
    System.out.println(thread1.getState());
    thread2.start();
    }
    }

    输出
    thread start.
    WAITING
    唤醒线程.....
    waiting closed.

  5. interrupted和isInterrupted
    Interrupted方法是一个静态方法,它检测当前的线程是否被中断。而且,调用interrupted方法会清除该线程的中断状态。
    另一方面,isInterrupted方法是一个实例方法,可用来检验是否有线程被中断。调用这个方法不会改变中断状态。

  6. setPriority和getPriority
    void setPriority(int newPriority)
    设置线程的优先级。优先级必须在Thread.MIN_PRIORITY 与Thread.MAX_PRIORITY之
    间。一般使用Thread.NORM_PRIORITY 优先级。
    final int getPriority()
    获取线程的优先级

  7. 结束线程
    Tread中有stop方法,但改方法已经过时,不推荐使用。要结束线程,可设置标志来实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Test {
    class TestTread implements Runnable{
    private boolean flag;
    private void stopThread() {
    this.flag=true;
    }
    @Override
    public void run() {
    for(int i=0; i<1000 && !flag; i++){
    System.out.println("i is "+i);
    }
    }
    }

    public static void main(String[] args) throws InterruptedException {
    TestTread test = new Test().new TestTread();
    Thread thread1 = new Thread(test);
    thread1.start();
    Thread.sleep(3);//休眠延时
    test.stopThread();
    }
    }

    输出一段i的值之后当执行stopThread方法线程就结束了。