M 多线程
2020-11-24 06:21:28 49 庶卒 Java 参与编辑 版本: 1 引用率 22.12%
## Java中的线程

Java之父对线程的定义是:
> 线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进程所创建的对象资源(内存资源)。java.lang.Thread对象负责统计和控制这种行为。

> 每个程序都至少拥有一个线程-即作为Java虚拟机(JVM)启动参数运行在主类main方法的线程。在Java虚拟机初始化过程中也可能启动其他的后台线程。这种线程的数目和种类因JVM的实现而异。然而所有用户级线程都是显式被构造并在主线程或者是其他用户线程中被启动。

      本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。在这之前,首先让我们来了解下在操作系统中进程和线程的区别:
    
      进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)
    
      线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
    
      线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
    
      多进程是指操作系统能同时运行多个任务(程序)。
    
      多线程是指在同一程序中有多个顺序流在执行。
    
    在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用

## Java线程状态机


Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

* * *

### 一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

![](https://www.runoob.com/wp-content/uploads/2014/01/java-thread.jpg)

*   **新建状态:**

    使用 **new** 关键字和 **Thread** 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 **start()** 这个线程。

*   **就绪状态:**

    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

*   **运行状态:**

    如果就绪状态的线程获取 CPU 资源,就可以执行 **run()**,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

*   **阻塞状态:**

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    *   等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

    *   同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

    *   其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

*   **死亡状态:**

    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
## Java多线程实战
## 多线程的实现

```java100.00%      
public class 多线程实例 {
    //继承thread
    @Test
    public void test1() {100.00%      
        class A extends Thread {
            @Override100.00%      
            public void run() {100.00%      
                System.out.println("A run");
            }
        }
        A a = new A();
        a.start();
    }
    //实现Runnable
    @Test
    public void test2() {
        class B implements Runnable {
            @Override100.00%      
            public void run() {100.00%      
                System.out.println("B run");
            }
        }
        B b = new B();100.00%      
        //Runable实现类需要由Thread类包装后才能执行
        new Thread(b).start();
    }
    //有返回值的线程67.08%      
    @Test
    public void test3() {
        Callable callable = new Callable() {100.00%      
            int sum = 0;
            @Override100.00%      
            public Object call() throws Exception {100.00%      
                for (int i = 0;i < 5;i ++) {
                    sum += i;
                }
                return sum;
            }
        };
        //这里要用FutureTask,否则不能加入Thread构造方法
        FutureTask futureTask = new FutureTask(callable);100.00%      
        new Thread(futureTask).start();
        try {100.00%      
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {100.00%      
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    //线程池实现
    @Test
    public void test4() {
        ExecutorService executorService = Executors.newFixedThreadPool(5);100.00%      
        //execute直接执行线程
        executorService.execute(new Thread());
        executorService.execute(new Runnable() {
            @Override100.00%      
            public void run() {100.00%      
                System.out.println("runnable");
            }
        });
        //submit提交有返回结果的任务,运行完后返回结果。
        Future future = executorService.submit(new Callable() {76.98%      
            @Override100.00%      
            public String call() throws Exception {100.00%      
                return "a";
            }
        });
        try {100.00%      
            System.out.println(future.get());
        } catch (InterruptedException e) {100.00%      
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        ArrayList list = new ArrayList<>();83.33%      
        //有返回值的线程组将返回值存进集合65.08%      
        for (int i = 0;i < 5;i ++ ) {
            int finalI = i;100.00%      
            Future future1 = executorService.submit(new Callable() {73.03%      
                @Override100.00%      
                public String call() throws Exception {100.00%      
                    return "res" + finalI;
                }
            });
            try {100.00%      
                list.add((String) future1.get());
            } catch (InterruptedException e) {100.00%      
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        for (String s : list) {
            System.out.println(s);
        }
    }
}
```

## 线程状态转换

```java100.00%      
public class 线程的状态转换 {
//一开始线程是init状态,结束时是terminated状态
class t implements Runnable {
    private String name;100.00%      
    public t(String name) {
        this.name = name;
    }
    @Override
    public void run() {100.00%      
        System.out.println(name + "run");
    }
}
//测试join,父线程在子线程运行时进入waiting状态
@Test
public void test1() throws InterruptedException {
    Thread dad = new Thread(new Runnable() {
        Thread son = new Thread(new t("son"));
        @Override100.00%      
        public void run() {100.00%      
            System.out.println("dad init");
            son.start();
            try {100.00%      
                //保证子线程运行完再运行父线程
                son.join();
                System.out.println("dad run");
            } catch (InterruptedException e) {100.00%      
                e.printStackTrace();
            }
        }
    });
    //调用start,线程进入runnable状态,等待系统调度
    dad.start();
    //在父线程中对子线程实例使用join,保证子线程在父线程之前执行完

}

//测试sleep
@Test
public void test2(){
    Thread t1 = new Thread(new Runnable() {
        @Override100.00%      
        public void run() {100.00%      
            System.out.println("t1 run");
            try {100.00%      
                Thread.sleep(3000);100.00%      
            } catch (InterruptedException e) {100.00%      
                e.printStackTrace();
            }
        }
    });

    //主线程休眠。进入time waiting状态
    try {100.00%      
        Thread.sleep(3000);100.00%      
    } catch (InterruptedException e) {100.00%      
        e.printStackTrace();
    }
    t1.start();

}

//线程2进入blocked状态。
public static void main(String[] args) {100.00%      
    test4();
    Thread.yield();//进入runnable状态
}

//测试blocked状态
public static void test4() {
    class A {
        //线程1获得实例锁以后线程2无法获得实例锁,所以进入blocked状态
        synchronized void run() {
            while (true) {100.00%      
                System.out.println("run");
            }
        }
    }
    A a = new A();
    new Thread(new Runnable() {100.00%      
        @Override100.00%      
        public void run() {100.00%      
            System.out.println("t1 get lock");
            a.run();
        }
    }).start();100.00%      
    new Thread(new Runnable() {100.00%      
        @Override100.00%      
        public void run() {100.00%      
            System.out.println("t2 get lock");
            a.run();
        }
    }).start();100.00%      

}

//volatile保证线程可见性
volatile static int flag = 1;100.00%      
//object作为锁对象,用于线程使用wait和notify方法100.00%      
volatile static Object o = new Object();100.00%      
//测试wait和notify
//wait后进入waiting状态,被notify进入blocked(阻塞等待锁释放)或者runnable状态(获取到锁)
public void test5() {
    new Thread(new Runnable() {100.00%      
        @Override100.00%      
        public void run() {100.00%      
            //wait和notify只能在同步代码块内使用100.00%      
            synchronized (o) {100.00%      
                while (true) {100.00%      
                    if (flag == 0) {
                        try {100.00%      
                            Thread.sleep(2000);100.00%      
                            System.out.println("thread1 wait");
                            //释放锁,线程挂起进入object的等待队列,后续代码运行100.00%      
                            o.wait();
                        } catch (InterruptedException e) {100.00%      
                            e.printStackTrace();
                        }
                    }
                    System.out.println("thread1 run");
                    System.out.println("notify t2");
                    flag = 0;100.00%      
                    //通知等待队列的一个线程获取锁100.00%      
                    o.notify();
                }
            }
        }
    }).start();100.00%      
    //解释同上100.00%      
    new Thread(new Runnable() {100.00%      
        @Override100.00%      
        public void run() {100.00%      
            while (true) {100.00%      
                synchronized (o) {100.00%      
                    if (flag == 1) {
                        try {100.00%      
                            Thread.sleep(2000);100.00%      
                            System.out.println("thread2 wait");
                            o.wait();
                        } catch (InterruptedException e) {100.00%      
                            e.printStackTrace();
                        }
                    }
                    System.out.println("thread2 run");
                    System.out.println("notify t1");
                    flag = 1;100.00%      
                    o.notify();
                }
            }
        }
    }).start();100.00%      
}

//输出结果是
//    thread1 run100.00%      
//    notify t2100.00%      
//    thread1 wait100.00%      
//    thread2 run100.00%      
//    notify t1100.00%      
//    thread2 wait100.00%      
//    thread1 run100.00%      
//    notify t2100.00%      
//不断循环100.00%      

}
```
## Java Thread常用方法

### Thread.yield():

执行此方法会向系统线程调度器(Schelduler)发出一个暗示,告诉其当前JAVA线程打算放弃对CPU的使用,但该暗示,有可能被调度器忽略。使用该方法,可以防止线程对CPU的过度使用,提高系统性能。

### Thread.sleep(time)或Thread.sleep(time, nanos):

使当前线程进入休眠阶段,状态变为:TIME_WAITING

### Thread.interrupt():

中断当前线程的执行,允许当前线程对自身进行中断,否则将会校验调用方线程是否有对该线程的权限。69.63%      

如果当前线程因被调用`Object.wait()`,`Object.wait(long, int)`, 或者线程本身的`join()`,` join(long)`,`sleep()`处于阻塞状态中,此时调用`interrupt()`方法会使抛出`InterruptedException`,而且线程的阻塞状态将会被清除。

### Thread.interrupted(),返回true或者false:

查看当前线程是否处于中断状态,这个方法比较特殊之处在于,如果调用成功,会将当前线程的`interrupt status`清除。所以如果连续2次调用该方法,第二次将返回false。

### Thread.isInterrupted(),返回true或者false:

与上面方法相同的地方在于,该方法返回当前线程的中断状态。不同的地方在于,它不会清除当前线程的`interrupt status`状态。

### Thread.join(),Thread.join(time):

A线程调用B线程的`join()`方法,将会使A等待B执行,直到B线程终止。如果传入`time`参数,将会使A等待B执行time的时间,如果time时间到达,将会切换进A线程,继续执行A线程。


## 构造方法和守护线程

    构造方法
    Thread类中不同的构造方法接受如下参数的不同组合:61.98%      
    
    一个Runnable对象,这种情况下,Thread.start方法将会调用对应Runnable对象的run方法。如果没有提供Runnable对象,那么就会立即得到一个Thread.run的默认实现。
    
    一个作为线程标识名的String字符串,该标识在跟踪和调试过程中会非常有用,除此别无它用。
    
    线程组(ThreadGroup),用来放置新创建的线程,如果提供的ThreadGroup不允许被访问,那么就会抛出一个SecurityException 。

  


    Thread对象拥有一个守护(daemon)标识属性,这个属性无法在构造方法中被赋值,但是可以在线程启动之前设置该属性(通过setDaemon方法)。
    
    当程序中所有的非守护线程都已经终止,调用setDaemon方法可能会导致虚拟机粗暴的终止线程并退出。
    
    isDaemon方法能够返回该属性的值。守护状态的作用非常有限,即使是后台线程在程序退出的时候也经常需要做一些清理工作。
    
    (daemon的发音为”day-mon”,这是系统编程传统的遗留,系统守护进程是一个持续运行的进程,比如打印机队列管理,它总是在系统中运行。)

## 启动线程的方式和isAlive方法

启动线程
调用start方法会触发Thread实例以一个新的线程启动其run方法。新线程不会持有调用线程的任何同步锁。

当一个线程正常地运行结束或者抛出某种未检测的异常(比如,运行时异常(RuntimeException),错误(ERROR) 或者其子类)线程就会终止。

**当线程终止之后,是不能被重新启动的。在同一个Thread上调用多次start方法会抛出InvalidThreadStateException异常。**

如果线程已经启动但是还没有终止,那么调用isAlive方法就会返回true.即使线程由于某些原因处于阻塞(Blocked)状态该方法依然返回true。

如果线程已经被取消(cancelled),那么调用其isAlive在什么时候返回false就因各Java虚拟机的实现而异了。没有方法可以得知一个处于非活动状态的线程是否已经被启动过了。

## Java多线程优先级

**Java的线程实现基本上都是内核级线程的实现,所以Java线程的具体执行还取决于操作系统的特性。**

Java虚拟机为了实现跨平台(不同的硬件平台和各种操作系统)的特性,Java语言在线程调度与调度公平性上未作出任何的承诺,甚至都不会严格保证线程会被执行。但是Java线程却支持优先级的方法,这些方法会影响线程的调度:

每个线程都有一个优先级,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间(分别为1和10)
默认情况下,新创建的线程都拥有和创建它的线程相同的优先级。main方法所关联的初始化线程拥有一个默认的优先级,这个优先级是Thread.NORM_PRIORITY (5).

线程的当前优先级可以通过getPriority方法获得。
线程的优先级可以通过setPriority方法来动态的修改,一个线程的最高优先级由其所在的线程组限定。

## Java多线程面试题60.00%      

这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。

这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过,但是本文写作的重心就是所有的问题都会按照自己的理解回答一遍,不会去看网上的答案,因此可能有些问题讲的不对,能指正的希望大家不吝指教。

> **1、多线程有什么用?**

一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个问题的看法:

**1)发挥多核CPU的优势**

随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4核、8核甚至16核的也都不少见,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。**单核CPU上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了**。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。

**2)防止阻塞**

从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。

**3)便于建模**

这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。

> **2、创建线程的方式**

比较常见的一个问题了,一般就是两种:

1)继承Thread类

2)实现Runnable接口

至于哪个好,不用说肯定是后者好,因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,**面向接口编程**也是设计模式6大原则的核心。


> **3、start()方法和run()方法的区别**

只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。61.64%      

> **4、Runnable接口和Callable接口的区别**

有点深的问题了,也看出一个Java程序员学习知识的广度。

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。67.14%