Java中使用线程都有哪几种方式?
一、通过继承Thread类实现
/**
* 定义一个奔跑线程类,让它跑起来
**/
public class RuningThread extends Thread {
@Override
public void run() {
System.out.println("这是一个线程类-跑起来");
}
}
public class ApplicationMain {
public static void main(String[] args) {
RuningThread thread = new RuningThread();
thread.start();
}
}
⚠️继承Thread类需要重写run()方法
二、实现Runnable接口
public class RunningImRunnable implements Runnable{
@Override
public void run() {
System.out.println("RunningImRunnable");
}
}
public class ApplicationMain {
public static void main(String[] args) {
Thread thread = new Thread(new RunningImRunnable());
thread.start();
}
}
对比第一种方式,是不是实现类的方式更好呢?,众所周知Java中类是单继承的,而接口可以多实现
三、实现Callable接口
public class RunningCallAble implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("我在干嘛");
return "结束啦?";
}
}
public class ApplicationMain {
public static void main(String[] args) {
RunningCallAble runningCallAble = new RunningCallAble();
FutureTask<String> futureTask = new FutureTask<>(runningCallAble);
Thread thread = new Thread(futureTask);
}
}
四、线程池ExecutorService
public class ApplicationMain {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(()->{
System.out.println("线程运行");
});
}
}
不推荐这么使用线程池!具体为什么 看下源码
看Executors源码实际创建的是ThreadPoolExecutor
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
看下ThreadPoolExecutor类的构造函数的参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
- corePoolSize:核心线程池大小,即使它们处于空闲状态,也要保持在池中,除非设置了 allowCoreThreadTimeOut;
- maximumPoolSize 池中允许的最大线程数;
- keepAliveTime:当线程数量超过核心大小时,额外的空闲线程在终止前等待新任务的最大时间;
- workQueue:用于在执行前持有任务的队列 此队列将仅持有 execute方法提交的Runnable任务;
- threadFactory:当执行器创建新线程时要使用的工厂;
- handler:当由于线程界限和队列容量达到而阻塞执行时要使用的处理器;
Executors.newFixedThreadPool(1)中new ThreadPoolExecutor的时候
workQueue任务队列设置的LinkedBlockingQueue基于链表的阻塞队列,默认无界,所以在使用他的时候很有可能出现OOM内存溢出的情况;
如何处理呢?
所以我们可以直接new一个ThreadPoolExecutor,并且指定一个有界的阻塞队列ArrayBlockingQueue
public class ApplicationMain {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(10,
10,1000, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(10));
executorService.execute(()->{
System.out.println("线程运行");
});
}
}
线程的状态
线程的6种状态,在Thread线程类的State枚举对象里可以看到
- NEW(新建): 线程刚被创建,但尚未启动。
- 此时线程对象已经存在,但尚未调用start()方法。
- RUNNABLE(可运行): 线程已经启动,并且正在执行或等待CPU资源。
- 这个状态包括两种情况:
- Ready(就绪):线程已经准备好运行,等待CPU分配时间片。
- Running(运行):线程正在CPU上执行。 BLOCKED(阻塞): 线程因为等待获取一个监视器锁(monitor
lock)而被阻塞。通常发生在同步代码块或方法中,线程试图获取一个已经被其他线程持有的锁。
- 这个状态包括两种情况:
- WAITING(等待):
- 线程处于等待状态,直到其他线程显式地唤醒它。
- 线程可以通过以下方法进入等待状态:
- Object.wait() Thread.join()
- LockSupport.park() 在这种状态下,线程不会消耗CPU资源。
- TIMED_WAITING(计时等待):
- 线程处于等待状态,但有一个指定的超时时间。
- 线程可以通过以下方法进入计时等待状态:
- Thread.sleep(long millis)
- Object.wait(long timeout) Thread.join(long millis)
- LockSupport.parkNanos(long nanos) LockSupport.parkUntil(long deadline) 在超时时间到达后,线程会自动唤醒。
- TERMINATED(终止):
- 线程已经执行完毕,或者因为异常而终止。
- 线程一旦进入终止状态,就不能再被启动
线程池的状态
状态 | 说明 |
---|---|
RUNNING | 会接受任务,并且也会执行队列里面的任务 |
SHUTDOWN | 该状态下不会接受新的任务,并且会执行完队列里面的任务,任务处理完成后终止线程 |
STOP | 不会接受新的任务,并且不会执行队列的里面的任务,会直接中断所有线程 |
TIDYING | 所有线程听着后,线程池会进入TIDYING状态,一但进入此状态 就会调用一个Terminated()方法 |
TERMINATED | Terminated方法执行完成后进入TERMINATED状态 |