java线程

java线程

生命周期

New:线程正在创建

Runnable:线程可以运行(可能在等待时间片或正在运行)

Blocking:线程等待获取锁

Waiting:当前线程等待其他线程唤醒

Timed Waiting:当前线程等待一段时间后会自动醒来

Terminated:线程执行完毕或出现异常

线程接口

java中有三种线程的接口/类:

  • Thread类
  • Runnable接口
  • Callable接口

使用

Thread

1
2
3
4
5
6
7
8
9
public class MainApplication {
public static void main(String[] args) {
// 直接创建线程
Thread thread = new Thread(() -> {
System.out.println("线程执行");
});
thread.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class MainApplication extends Thread{
public static void main(String[] args) {
MainApplication mainApplication = new MainApplication();
mainApplication.start();
}

@Override
public void run() {
// 通过继承Thread来使用
System.out.println("线程执行");
}
}

Runnable

1
2
3
4
5
6
7
8
9
10
11
12
public class MainApplication implements Runnable{
public static void main(String[] args) {
// 实现Runnable接口
Thread thread = new Thread(new MainApplication());
thread.start();
}

@Override
public void run() {
System.out.println("线程执行");
}
}

Callable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainApplication implements Callable<Integer> {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 实现Callable接口,这种线程任务可以有返回值
FutureTask<Integer> futureTask = new FutureTask<>(new MainApplication());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}

@Override
public Integer call() throws Exception {
System.out.println("线程执行");
return 1;
}
}

API

静态方法

方法 说明
Thread.sleep() 休眠当前正在执行的线程
Thread.yield() 当前线程建议让出cpu,由调度器决定是否让出

实例方法

方法 说明
thread.setDaemon() true为设置线程为守护线程
thread.interrupt() 中断在休眠中的线程,会抛出异常;在运行的线程则会设置标记,由线程自己决定是否中断
thread.interrupted() 获取中断标记
thread.join() 调用线程等待被调用线程执行完才会继续执行
object.wait() 挂起线程,会释放锁,只能在同步代码块里调用
object.notify() 随机唤醒线程,只能在同步代码块里调用

interrupt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainApplication extends Thread {
public static void main(String[] args) {
MainApplication mainApplication = new MainApplication();
mainApplication.start();
mainApplication.interrupt(); // 中断线程
System.out.println("Main run");
}

@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("Thread run");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 控制台打印
// Main run

interrupted

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MainApplication extends Thread {
public static void main(String[] args) throws InterruptedException {
MainApplication mainApplication = new MainApplication();
mainApplication.start();
mainApplication.interrupt();
Thread.sleep(1000);
System.out.println("Main run");
}

@Override
public void run() {
while (!interrupted()) {

}
System.out.println("Thread run");
}
}

// 控制台打印
// Thread run
// Main run

线程互斥/协作

互斥方式

  • synchronized
  • Lock

协作方式

  • join()
  • wait()、notify() / notifyAll()
  • await()、single() / singleAll():在Condition上使用

线程池

对线程进行统一分配,调优和监控

ThreadPoolExecutor

​ 一个线程集合workerSet和一个阻塞队列workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workerSet中的线程会不断的从workQueue中获取线程然后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行。

获取线程池状态api

方法 说明
threadPoolExecutor.getPoolSize() 当前线程池中的线程数
threadPoolExecutor.getCorePoolSize() 当前线程池中核心线程的数量
threadPoolExecutor.getActiveCount() 正在执行任务的线程数
threadPoolExecutor.getCompletedTaskCount() 已经完成的任务数
threadPoolExecutor.getTaskCount() 线程池中已经提交的任务总数
threadPoolExecutor.isShutdown() 线程是否已经调用了shutdown方法
threadPoolExecutor.isTerminated() 线程是否已经执行完任务并关闭

创建线程池的参数

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,		// 核心线程数
int maximumPoolSize, // 允许的最大线程数=核心+非核心
long keepAliveTime, // 非核心存活时间
TimeUnit unit, // 存活时间单位
BlockingQueue<Runnable> workQueue, // 任务阻塞队列
RejectedExecutionHandler handler // 拒绝策略
)

任务阻塞队列种类:

  • ArrayBlockingQueue: 基于数组结构的有界阻塞队列,按FIFO排序任

  • LinkedBlockingQueue: 基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQueue;

  • SynchronousQueue: 一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue;

  • PriorityBlockingQueue: 具有优先级的无界阻塞队列;

拒绝策略种类:

  • AbortPolicy: 直接抛出异常,默认策略;

  • CallerRunsPolicy: 用调用者所在的线程来执行任务;

  • DiscardOldestPolicy: 丢弃阻塞队列中靠最前的任务,并执行当前任务;

  • DiscardPolicy: 直接丢弃任务

线程池状态

​ 线程池中通过原子整数ctl的前3位来记录线程池状态,后29位记录当前线程数

Executors

Executors把工作单元(Runnable)与执行机制分离开

通过Executors可以创建预设好参数的线程池,如固定大小线程池、只有救急线程的线程池等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainApplication implements Runnable {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
pool.execute(new MainApplication());
}
pool.shutdown();
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

api

方法 说明
pool.shutdown() 等所有任务执行完成后关闭线程池
pool.shutdownNow() 立即关闭线程池,所有线程执行interrupt()
pool.submit() 执行有返回值的任务
pool.execute() 执行没有返回值的任务

为什么不建议使用Executor创建线程?

​ 使用了无界队列,任务可以无限提交,会导致内存溢出;newCachedThreadPool如果频繁创建销毁线程会导致大量的系统开销和内存溢出

Condition

相当于等待队列

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
36
37
38
public class MainApplication {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

public static void main(String[] args) throws InterruptedException {
MainApplication mainApplication = new MainApplication();
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(() -> mainApplication.before());
pool.execute(() -> mainApplication.after());
pool.shutdown();
}

public void before() {
lock.lock();
try {
condition.await(); // 在condition上等待
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("before");
}

public void after() {
lock.lock();
try {
condition.signal(); // 唤醒condition上等待的线程
} finally {
lock.unlock();
}
System.out.println("after");
}
}

// 控制台打印
// after
// before

java线程
http://xwww12.github.io/2023/06/05/java/java线程/
作者
xw
发布于
2023年6月5日
许可协议