文章目录
一.线程
1.基本概念
- 现代操作系统调度的最小单元是线程,也叫轻量级进程
- 一个进程里可以创建多个线程
- 每一个线程都拥有各自的计数器,堆栈,局部变量表等属性,并且可以访问共享的内存变量
- 处理在线程之间告诉切换,使使用者感觉好像这些线程同时执行
2.一个普通的java程序包括哪些线程
一个java程序不仅仅是main主线程在运行,还有其他多个线程在同时运行,java程序本身就是一个多线程程序
@Slf4j
public class MultiThread {
public static void main(String[] args) {
// 获取java线程管理MXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 获取线程和线程的堆栈信息(不需要获取线程的Monitors,synchronizers)
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,false);
// 打印线程信息
for(ThreadInfo threadInfo:threadInfos){
log.info("threadName:--------->"+threadInfo.getThreadName());
}
}
}
控制台打印结果:
22:41:44.930 [main] INFO com.kevin.demo.base_of_cconcurrency.MultiThread - threadName:--------->Monitor Ctrl-Break
22:41:44.935 [main] INFO com.kevin.demo.base_of_cconcurrency.MultiThread - threadName:--------->Attach Listener
22:41:44.935 [main] INFO com.kevin.demo.base_of_cconcurrency.MultiThread - threadName:--------->Signal Dispatcher
22:41:44.935 [main] INFO com.kevin.demo.base_of_cconcurrency.MultiThread - threadName:--------->Finalizer
22:41:44.935 [main] INFO com.kevin.demo.base_of_cconcurrency.MultiThread - threadName:--------->Reference Handler
22:41:44.935 [main] INFO com.kevin.demo.base_of_cconcurrency.MultiThread - threadName:--------->main
各个基本线程的作用:
- Monitor Ctrl-Break
- Attach Listener : 线程的职责是接收外部jvm命令
- signal dispather : 去进行分发处理发送给JVM各个不同的模块处理命令
- Finalizer :调用对象该方法的线程
- Reference Handler :清除引用的线程
- main :主线程
二,创建线程
1.继承Thread类,重写run方法
线程执行的任务放于run方法中
@Slf4j
public class NewThread1 extends Thread{
@Override
public void run() {
while (true){
log.info("-----> Thread1111111111111111");
}
}
}
启动线程,通过调用Thread类的start()方法来启动一个线程。
// 线程构建方式一
NewThread1 newThread1 = new NewThread1();
newThread1.start();
调用run()和调用start()方法有什么不同?
-
调用start方法是开启一个新线程放入到等到队列中,一旦争夺到cpu资源便开始执行
-
调用子线程的run方法实际上并不会开启一个新的线程,还是在主线程中
2.实现Runnable接口,重写run方法
@Slf4j
public class NewThread2 implements Runnable{
@Override
public void run() {
while(true){
log.info("---------> newThread222222222222222");
}
}
}
启动线程,传入runnable子类对象
// 线程构建方式二
Thread thread = new Thread(new NewThread2());
thread.start();
3.带有返回值的多线程实现 — 实现callable接口
返回值类型就是Callable中的泛型参数类型T
@Slf4j
public class NewThread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int i = 0;
while(i<10000){
log.info("--------> newThread333333333333");
i++;
}
return i;
}
}
注意,这里跟前面两种方式的不同之处在于要借助一个中间类FutureTask
// 线程构建方式三
FutureTask futureTask = new FutureTask(new NewThread3());
Thread thread3 = new Thread(futureTask);
thread3.start();
//获取返回值
int i = (int) futureTask.get();
log.info("-------> "+i);
4.基于线程池
使用线程池可以降低资源消耗,提高响应速度和线程的可管理性
之后会另外总结
@Slf4j
public class NewThead4 {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService = new ThreadPoolExecutor(5, 10, 500,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
//执行任务
while (true) {
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("------------>newThread444444444444444");
}
});
}
}
}
测试结果:
线程之间在互相争夺cpu资源,谁抢到谁执行
三,线程状态
线程在自身的生命周期中并不是固定的处于某种状态,而是在随着代码的执行在不同状态之间切换
1.状态图
2.过程
-
线程创建
-
调用start()开始进入运行状态,但是此时并非马上就运行任务,需要等到系统cpu分配到当前线程资源才能执行任务,Thread.yield()方法作用是:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。
-
线程如果执行正常,将直接完成当前周期
-
若线程执行wait(),则进入等待状态,在这一状态中,需要依靠其他线程的通知(唤醒)才能返回运行状态
-
还有个就是超时等待状态,跟等待状态不同的是,超时等待状态经过指定时间就可以自动返回运行状态
-
阻塞,当线程遇到同步方法或者同步代码块,则进入阻塞状态