天天看点

并发编程 -- 线程构建及线程状态

文章目录

  • ​​一.线程​​
  • ​​1.基本概念​​
  • ​​2.一个普通的java程序包括哪些线程​​
  • ​​二,创建线程​​
  • ​​1.继承Thread类,重写run方法​​
  • ​​2.实现Runnable接口,重写run方法​​
  • ​​3.带有返回值的多线程实现 --- 实现callable接口​​
  • ​​4.基于线程池​​
  • ​​三,线程状态​​
  • ​​1.状态图​​
  • ​​2.过程​​

一.线程

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.过程

  1. 线程创建
  2. 调用start()开始进入运行状态,但是此时并非马上就运行任务,需要等到系统cpu分配到当前线程资源才能执行任务,Thread.yield()方法作用是:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。
  3. 线程如果执行正常,将直接完成当前周期
  4. 若线程执行wait(),则进入等待状态,在这一状态中,需要依靠其他线程的通知(唤醒)才能返回运行状态
  5. 还有个就是超时等待状态,跟等待状态不同的是,超时等待状态经过指定时间就可以自动返回运行状态
  6. 阻塞,当线程遇到同步方法或者同步代码块,则进入阻塞状态