目录
基本概念
进程
每一个正在运行着的程序都是一个进程
线程
一个进程中可以包含多个线程,可以将线程理解为应用程序中相互独立执行的功能,是操作系统能够运算调度的最小单位
多线程
应用程序中相互独立执行的功能比较多,就形成了多线程,可以让程序同时做多件事情,合理的线程数可以提高效率
并发
同一时刻,有多个指令在单个CPU上交替执行
并行
同一时刻,有多个指令在多个CPU上同时执行
多线程的实现方式
在Java中,有如下几种常见的实现多线程的方式
继承Thread类
代码示例
java
package thread;
/**
* 1、继承Thread类并重写run方法
* 2、创建线程对象
* 3、启动线程
*/
public class ExtendsThread {
public static void main(String[] args) {
// 创建线程对象
ChildThread t1 = new ChildThread();
ChildThread t2 = new ChildThread();
// 设置线程名字
t1.setName("线程1");
t2.setName("线程2");
// 启动线程
t1.start();
t2.start();
}
}
class ChildThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
// getName():获取当前线程名字
System.out.println(getName() + "-" + i);
}
}
}
执行结果
如下图,能看到线程的交替执行

实现Runnable接口
代码示例
java
package thread;
public class ImplementsRunnable {
public static void main(String[] args) {
// 创建任务对象
Task task = new Task();
// 创建线程对象,执行任务
Thread t1 = new Thread(task, "线程1");
Thread t2 = new Thread(task, "线程2");
// 开启线程
t1.start();
t2.start();
}
}
/**
* 自定义类实现Runnable接口并将要执行的任务放入run方法中
*/
class Task implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
// Thread.currentThread().getName():获取当前线程名字
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
}
执行结果
同样能看到线程交替执行的现象

利用FutureTask和Callable接口
Callable接口有返回值,通过该实现方式可以获取到执行结果
代码示例
java
package thread;
import java.util.concurrent.FutureTask;
public class CallableFuture {
public static void main(String[] args) throws Exception {
// 任务1
FutureTask<String> task1 = new FutureTask<>(() -> {
// 执行任务1需要耗时5s
Thread.sleep(5000);
return "任务1执行结束...";
});
// 任务2
FutureTask<String> task2 = new FutureTask<>(() -> {
// 执行任务2需要耗时3s
Thread.sleep(3000);
return "任务2执行结束...";
});
// 创建线程对象
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
long beginTime = System.currentTimeMillis();
// 开启线程执行任务
t1.start();
t2.start();
// 两个任务都执行完
System.out.println(task2.get());
System.out.println(task1.get());
long endTime = System.currentTimeMillis();
// 查看两个任务执行完后耗时情况
System.out.println("任务总耗时:" + ((endTime - beginTime) / 1000) + "s");
}
}
执行结果
如下图所示,执行任务1需要5s,执行任务2需要3s,执行完任务1和任务2总耗时为5s

线程的优先级
在计算机中,线程的调度分为两种,抢占式调度(多个线程在抢夺CPU的执行权)和非抢占式调度(所有的线程轮流执行),Java中采取的是抢占式调度,体现出一种随机性,线程的优先级就和这个随机性有关,线程的优先级越高,抢占到CPU的执行权的几率越大
查看源码
在Java中,线程的优先级分为1~10,如果不设置优先级的话,线程默认优先级为5
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
// 最小优先级
public final static int MIN_PRIORITY = 1;
// 默认优先级
public final static int NORM_PRIORITY = 5;
// 最大优先级
public final static int MAX_PRIORITY = 10;
// 设置优先级
public final void setPriority(int newPriority) {
// ...
}
// 获取优先级
public final int getPriority() {
return priority;
}
// ...
}
测试案例
java
package thread;
public class ThreadPriority {
public static void main(String[] args) {
// 当前线程是main线程,默认优先级为5
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());
System.out.println(currentThread.getPriority());
// 创建一个线程,未设置其优先级,默认优先级为5
Thread t1 = new Thread(() -> {
});
System.out.println(t1.getPriority());
// 创建一个线程,设置其优先级
Thread t2 = new Thread(() -> {
});
t2.setPriority(1);
System.out.println(t2.getPriority());
}
}
线程的生命周期
新建
创建了线程对象
就绪
- 新创建的线程对象执行了start方法,有执行资格但是没有执行权,不停地抢CPU
- 被其他线程抢走CPU的执行权
- sleep方法时间到或者其他阻塞方式结束
运行
抢到了CPU的执行权,有执行资格运行run方法中的代码
阻塞等待
执行了线程的sleep方法或者其他阻塞式方法
死亡
run方法执行完毕,线程死亡变成垃圾
线程安全问题
多个线程同时操作共享数据时,发生数据不一致的现象叫做线程安全问题
卖票案例
需求
使用3个售票窗口同时卖票,一共有500张票
代码实现
java
package thread;
public class SellTicketTask implements Runnable {
// 当前票的编号,从1开始
private int currTicketNo = 1;
// 最大票的编号为500
private static final int MAX_TICKET_NO = 500;
@Override
public void run() {
while (true) {
// 票卖完了
if (currTicketNo > MAX_TICKET_NO) {
break;
}
// 票没卖完,执行卖票逻辑
// 这里假设卖票过程需要耗时20ms
handleSellTicketTime(20);
String handleResult = String.format("%s-卖出了第%s张票", getName(), currTicketNo++);
System.out.println(handleResult);
}
}
/**
* 获取卖票窗口名字
*/
public String getName() {
return Thread.currentThread().getName();
}
public void handleSellTicketTime(long time) {
if (time <= 0) return;
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
java
package thread;
public class SellTicketTest {
public static void main(String[] args) {
// 卖票任务
SellTicketTask sellTicketTask = new SellTicketTask();
// 3个卖票窗口执行卖票
new Thread(sellTicketTask, "窗口一").start();
new Thread(sellTicketTask, "窗口二").start();
new Thread(sellTicketTask, "窗口三").start();
}
}
测试结果发现存在数据不一致问题
这里数据不一致问题体现在如下两点
- 存在多个窗口同时卖出同一张票

- 出现超卖情况

解决线程安全问题
synchronized
java
package thread;
public class SellTicketTask implements Runnable {
// 当前票的编号,从1开始
private int currTicketNo = 1;
// 最大票的编号为500
private static final int MAX_TICKET_NO = 500;
@Override
public void run() {
while (true) {
// 票卖完了
if (currTicketNo > MAX_TICKET_NO) {
break;
}
synchronized (Object.class) {
// double check
if (currTicketNo > MAX_TICKET_NO) {
break;
}
// 票没卖完,执行卖票逻辑
// 这里假设卖票过程需要耗时20ms
handleSellTicketTime(20);
String handleResult = String.format("%s-卖出了第%s张票", getName(), currTicketNo++);
System.out.println(handleResult);
}
}
}
/**
* 获取卖票窗口名字
*/
public String getName() {
return Thread.currentThread().getName();
}
public void handleSellTicketTime(long time) {
if (time <= 0) return;
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Lock
java
package thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicketTask implements Runnable {
// 当前票的编号,从1开始
private int currTicketNo = 1;
// 最大票的编号为500
private static final int MAX_TICKET_NO = 500;
// Lock锁
private final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
// 上锁
lock.lock();
try {
// 票卖完了
if (currTicketNo > MAX_TICKET_NO) {
break;
}
// 票没卖完,执行卖票逻辑
// 这里假设卖票过程需要耗时20ms
handleSellTicketTime(20);
String handleResult = String.format("%s-卖出了第%s张票", getName(), currTicketNo++);
System.out.println(handleResult);
} finally {
// 释放锁
lock.unlock();
}
}
}
/**
* 获取卖票窗口名字
*/
public String getName() {
return Thread.currentThread().getName();
}
public void handleSellTicketTime(long time) {
if (time <= 0) return;
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
原子类
java
package thread;
import java.util.concurrent.atomic.AtomicInteger;
public class SellTicketTask implements Runnable {
// 当前票的编号,从1开始(这里使用原子类AtomicInteger)
private final AtomicInteger currTicketNo = new AtomicInteger(1);
// 最大票的编号为500
private static final int MAX_TICKET_NO = 500;
@Override
public void run() {
while (true) {
int currNo = currTicketNo.get();
// 票卖完了
if (currNo > MAX_TICKET_NO) {
break;
}
// 票没卖完,执行卖票逻辑
// 这里假设卖票过程需要耗时20ms
handleSellTicketTime(20);
// 比较并交换
if (currTicketNo.compareAndSet(currNo, (currNo + 1))) {
String handleResult = String.format("%s-卖出了第%s张票", getName(), currNo);
System.out.println(handleResult);
}
}
}
/**
* 获取卖票窗口名字
*/
public String getName() {
return Thread.currentThread().getName();
}
public void handleSellTicketTime(long time) {
if (time <= 0) return;
try {
Thread.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
线程的状态
概述
在Java中,定义了线程的6种状态
- 新建状态:创建线程对象
- 就绪状态:start方法等待CPU调度
- 阻塞状态:无法获得锁对象
- 等待状态:wait方法
- 计时等待状态:sleep方法
- 结束状态:全部代码运行完毕
查看源码
java
package java.lang;
// ...
public
class Thread implements Runnable {
// ...
public enum State {
// 新建状态
NEW,
// 就绪状态
RUNNABLE,
// 阻塞状态
BLOCKED,
// 等待状态
WAITING,
// 计时等待状态
TIMED_WAITING,
// 结束状态
TERMINATED;
}
// ...
}