文章目录
- 一、什么是多线程?
- 二、多线程的实现方法
- 1. 继承Thread类,重写run方法
- 2. 实现Runnable接口,并创建Thread对象
- 3. Callable和Future
- 三、线程的5种状态
- **New(新创建)**
- **Runnalbe(可运行)**
- **Running**
- **Blocked(阻塞)**
- **等待(Waiting)**
- **锁定(Lock)**
- **死亡(Dead)**
- 四、线程池的三大方法、七大参数、四种拒绝策略
- 线程池的三大方法:
- 线程池的七大参数:
- 线程的四大拒绝策略:
- 五、线程池的五种状态
- RUNNING
- SHUTDOWN
- STOP
- TIDYING
- TERMINATED
一、什么是多线程?
在 Java 中,多线程是指在一个进程中同时执行多个线程,每个线程都可以独立地执行特定的任务。线程是轻量级的进程,它们共享同一个进程的资源,包括内存空间、文件句柄等。通过多线程,可以提高程序的并发性和性能,因为多个线程可以同时执行,从而更好地利用 CPU 资源。
简单来说: 就是老板请员工帮你去做事。
二、多线程的实现方法
1. 继承Thread类,重写run方法
class ExtendThread extends Thread { // 继承自Threadprivate String name;public ExtendThread(String name) {this.name = name;}@Overridepublic void run() { // 必须重写run方法,并且将线程任务放到run里执行for (int i = 0; i < 5; i++) {System.out.println(name + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Example {public static void main(String[] args) {ExtendThread t1 = new ExtendThread("线程A");ExtendThread t2 = new ExtendThread("线程B");t1.start();t2.start();}
}
2. 实现Runnable接口,并创建Thread对象
class MyRunnable implements Runnable { // 必须要实现Runnable接口private String name;public MyRunnable(String name) {this.name = name;}public void run() { // 必须要有run方法,并且将需要执行的任务放到run方法里for (int i = 0; i < 5; i++) {System.out.println(name + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Example {public static void main(String[] args) {MyRunnable run1 = new MyRunnable("线程A");MyRunnable run2 = new MyRunnable("线程B");Thread t1 = new Thread(run1);Thread t2 = new Thread(run2);t1.start();t2.start();}
}
3. Callable和Future
前面创建线程的2种方式,都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
Callable和Future,它俩 一个产生结果,一个拿到结果。 Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,因而Callable功能更强些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。
public interface Callable<V> {V call() throws Exception;
}
三、线程的5种状态
New(新创建)
当用new创建一个新线程时,如new Thread®,该线程还没有开始运行,此时的状态为新创建状态。此时,程序还没有开始运行线程中的代码,在线程运行之前还有一些基本工作要做。
Runnalbe(可运行)
一旦调用start方法,线程便进入了可运行状态。一个可运行的线程可能正在运行,也可能没有运行,这取决于系统在该时间片里是否调度了该线程。对于常用的抢占式调度系统,它会给每一个可运行线程一个时间片来执行任务,当时间片用完时,操作系统便剥夺该线程的运行权,并给另一个线程运行机会。当选择下一个线程时,操作系统会考虑线程的优先级。不过也有一些操作系统,如像手机,采用的可能是协作式调度系统,在这样的系统中,一个线程只有在调用yield方法、或者被阻塞、或者被等待时,线程才失去控制权。
Running
就是运行状态
Blocked(阻塞)
对于阻塞状态,又可以根据阻塞原因的不同,将其细分为三种类型,分别是等待(Waiting)、锁定(Lock)和其他。处于阻塞状态的线程共同点都是暂时不活动,直到调度器重新激活它。
等待(Waiting)
正在运行的线程内部调用wait()时,该线程便进入阻塞状态(wait()是Object类的方法),并且在wait所在的代码行处停止执行,直到接到通知(notify)或被中断(等待时间到)为止。
需要注意的是:在调用wait方法之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait方法。
锁定(Lock)
当一个线程视图获取一个内部的对象锁,而该锁却被其他线程持有时,此时这个线程就进入阻塞状态,当所有其他线程释放该锁,并且线程调度器允许该线程持有它的时候,该线程才会变为Runnalbe状态。
其他
另外,还有如果在线程内部调用了sleep()或者jion()方法时,也会使该线程进入阻塞状态。sleep()是指让这个线程休息一段时间,时间到了之后再进入Runnalbe,jion()一般用在主线程中,表示主线程等待子线程把活干完了,再接着往下执行。
死亡(Dead)
线程进入死亡状态主要有以下两种原因:第一,run方法运行完毕,正常退出;第二,因为一个没有捕获的异常终止了run方法,导致线程意外死亡。
四、线程池的三大方法、七大参数、四种拒绝策略
线程池的三大方法:
ExecutorService service = Executors
.newSingleThreadExecutor();//单个线程
.newFixedThreadPool(5);//创建一个固定的线程池的大小
.newCachedThreadPool();//缓存线程池,可伸缩的
线程池的七大参数:
public ThreadPoolExecutor
(
int corePoolSize,//核心线程数
int maximumPoolSize,//最大的线程数
long keepAliveTime,//存活时间
TimeUnit unit,//存活时间的时间单位
BlockingQueue workQueue//阻塞队列)
ThreadFactory threadFactory,//线程工厂,创建线程的,一般不动
RejectedExecutionHandler handler//拒绝策略
)
线程的四大拒绝策略:
第一种拒绝策略:new ThreadPoolExecutor.AbortPolicy(),如果线程满了,则不处理新的进程,抛出异常;
第二种处理策略:new ThreadPoolExecutor.CallerRunsPolicy(),线程池满了,如果有新的哪里来的去哪里,不会抛出异常
第三种处理策略:new ThreadPoolExecutor.DiscardPolicy(),线程池满了,丢掉线程,不会抛出异常
第四种处理策略:
阿里巴巴开发手册对于线程池使用的规范: oom-> out of memory 内存耗尽
五、线程池的五种状态
线程池的五种状态:
Running、Shutdown、Stop、Tidying、Terminated
分别为:
RUNNING
RUNNING;
● 线程池处于RUNNING状态时,线程池能够接收新任务,也能够对已经添加的任务进行处理;
● 线程池一被创建,线程池的状态就是RUNNING状态;
SHUTDOWN
SHUTDOWN;
● 线程池已经被关闭了,不再接收新任务;但是,其还是会处理队列中的剩余的任务;
● 调用线程池的shutdown()方法后,线程池的状态就会由RUNNING转为SHUTDOWN;
STOP
STOP;
● 线程池处于STOP状态,此时线程池不再接收新任务,不处理已经添加进来的任务,并且会中断正在处理的任务;
● 调用线程池的shutdownNow()方法后,线程池的状态就会由RUNNING或SHUTDOWN转为STOP;
TIDYING
TIDYING;
● 线程池被下达关闭命令后,如果当前所有的任务都已经终止了(这个终止可以表示执行结束,也可以表示强制中断,也可以表示被丢弃) ,那么线程就会进入TIDYING状态;当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
● 如果线程状态已经是SHUTDOWN了,并且线程中以及队列中都没有任务时,线程池就会由SHUTDOWN转为TIDYING;如果线程池状态为STOP,那么当线程池把所有的任务都给清理干净时,线程池就会由STOP转为TIDYING;
TERMINATED
TERMINATED;
● 线程池就结束了;线程池就不能重新启动了;
● 如果线程池处于TIDYING状态,那么当线程池执行完terminated()方法后,线程池状态就会由TIDYING转为TERMINTED;