Java并发编程的基础是多线程。Java从诞生开始就明智地选择了内置对多线程的支持,这使得Java语言相比于同一时期的其他语言具有明显优势。
### 1、线程
#### 1.1、线程简介
现代操作系统在运行一个程序时,会为其创建一个进程,其调度的最小单元时线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且不能够访问共享的内存变量。在Java中,默认的线程有两个,分别是mian线程和GC线程。
#### 1.2、进程和线程
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。
#### 1.3、线程优先级
一个线程的线程优先级可以决定其需要多或者少分配一些处理器资源。在Java线程中,通过整型成员变量priority来控制线程优先级。优先级的范围是1~10;优先级的最大值为10,最小值为1,默认值为5。Java通过setPriority(int newPriority)方法为线程设置优先级。
```java
/**
* The minimum priority that a thread can have.
*/
public static final int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public static final int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public static final int MAX_PRIORITY = 10;
```
#### 1.4、线程状态
Java线程在运行的生命周期中可能处于6中不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。
|状态名称|说明|
|-------|-------|
|New|初始状态,线程被创建,但还没有调用start()方法|
|RUNNABLE|运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中”|
|BLOCKED|阻塞状态,表示线程阻塞于锁|
|WAITING|等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一定的动作(通知或中断)|
|TIME_WAITING|超时等待状态,该状态不同于WAITING,它是可以在值定的时间自行返回的|
|TERMINATED|终止状态,表示当前线程已执行完毕|
#### 1.5、Daemon线程
Daemon线程是一种支撑性线程,当一个Java虚拟机不存在Daemon线程时,Java虚拟机将会退出。讲一个线程设置为Daemon线程可以调用Thread.setDaemon(boolean on)方法,并将on赋值为true。
### 2、线程的创建
Java中创建线程主要有三种方式:
- 继承Thread类
- 实现Runnable接口
- 使用Callable和Future
这里需要注意的一点是Java本身并不能直接开启线程去调度计算机资源的,是因为Java的线程是作用在jvm虚拟机上的,并且Java通过一个本地方法来启动线程的,而这个本地方法是用C++写的。
```java
//启动一个线程调用的start()方法
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
//真正启动一个线程的方法
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//这是一个本地方法
private native void start0();
```
#### 2.1、继承Thead类创建线程
这种方式创建线程的步骤是:首先定义一个类为Thread类的子类,然后重写run()方法,最后通过实例化该类创建一个线程。
```java
package threadDemo.ArtOfConcurency;
/**
* @author miantiao
* @time 2020年3月26日 下午12:45:57
*/
public class ThreadDemo {
public static void main(String[] args) {
//通过继承Thread类创建的线程
Thread thread1 = new ThreadByExt();
}
}
```
```java
package threadDemo.ArtOfConcurency;
/**
* @author miantiao
* @time 2020年3月26日 下午1:00:10
*/
public class ThreadByExt extends Thread {
@Override
public void run() {
System.out.println("this is a new Thread created by inheriting from the Thread class");
}
}
```
#### 2.2、实现Runnable接口创建线程
这种方式创建线程的步骤是:首先定义一个类并实现Runnable接口,然后重写run()方法。然后将该类的实例化对象作为Thread类的构造器的target参数来创建一个线程。
```java
package threadDemo.ArtOfConcurency;
/**
* @author miantiao
* @time 2020年3月26日 下午12:45:57
*/
public class ThreadDemo {
public static void main(String[] args) {
//通过继承Thread类创建的线程
Thread thread2 = new Thread(new ThreadByRunnable());
}
}
```
```java
package threadDemo.ArtOfConcurency;
/**
* @author miantiao
* @time 2020年3月26日 下午1:06:11
*/
public class ThreadByRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("this is a new Thread Created by implementing the Runable interface!");
}
}
```
#### 2.3、使用Callable接口和Future接口创建线程
与Runnable接口的run()方法不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大,因为call()方法可以有返回值,也可以声明抛出异常。以下是JDK11中对Callable接口的定义。
```java
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
```
Java提供了Future接口来接收Callable接口中call()方法的返回值。Callable接口是Java5新增的接口,不是Runnable接口的子接口,所以Callable对象不能直接作为Thread对象的target。针对这个问题,引入了RunnableFuture接口,RunnableFuture接口是Runnable接口和Future接口的子接口,可以作为Thread对象的target。以下是JDK11中对RunnableFuture接口的定义。
```java
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
```
同时,Java提供了一个RunnableFuture接口的实现类:FutureTask ,FutureTask可以作为Thread对象的target。
使用Callable和Future创建线程的步骤如下:
(1)定义一个类实现Callable接口,并重写call()方法,该call()方法将作为线程执行体,并且有返回值
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象
(3)使用FutureTask对象作为Thread对象的target创建并启动线程
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
```java
package threadDemo.ArtOfConcurency;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author miantiao
* @time 2020年3月26日 下午12:45:57
*/
public class ThreadDemo {
public static void main(String[] args) {
// 通过Callable和Future接口创建的线程
ThreadByCallable callableTest = new ThreadByCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callableTest);
new Thread(futureTask).start();
try {
System.out.println("线程的返回值: " + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
```
```java
package threadDemo.ArtOfConcurency;
import java.util.concurrent.Callable;
/**
* @author miantiao
* @time 2020年3月26日 下午1:27:32
*/
public class ThreadByCallable implements Callable{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i < 101; i++) {
sum += i;
}
System.out.println(Thread.currentThread().getName() + " is running: " + sum);
return sum;
}
}
```
本节参考:[Java创建线程的三种方式及对比](https://juejin.im/post/5d6e5d0be51d4561d54de9d9)
作者:[指尖上的榴莲](https://juejin.im/user/5d6e5c136fb9a06b2766e933)
### 3、线程的启动、中断和终止
#### 3.1、启动
线程对象在初始化完成之后,调用start()方法就可以启动这个线程。线程start()方法的含义是:当前线程同步告知java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。以上节提到的通过实现Runnable接口创建的线程为例,其启动方式如下:
```java
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author miantiao
* @time 2020年3月26日 下午12:45:57
*/
public class ThreadDemo {
public static void main(String[] args) {
// 通过实现Runnable接口创建的线程
Thread thread2 = new Thread(new ThreadByRunnable());
// 启动线程
thread2.start();
}
}
```
#### 3.2、中断
中断可以理解为线程的一个表示为属性,它表示一个运行中的线程是否被其他线程进行了终止操作。中断一个线程可以通过调用interrupted()方法。线程通过isInterrupt()来进行判断是否被中断,也可以调动静态方法Thread.interrupted()对当前线程的中断位进行复位。如果该线程已经处于终结状态,即线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。
#### 3.3、安全地终止线程
线程的中断状态是线程的一个标识位,而中断操作是一种简便的线程间交互方式,而这种交互方式最适合取消和停止任务。安全的终止线程的方法有两种:(1)通过interrupt()方法中断线程;(2)通过boolean变量来控制是否需要停止并终止线程。

Java并发编程学习笔记(一)