JUC并发编程基础(包含线程概念,状态等具体实现)

一.JUC并发编程基础

1. 并行与并发

1.1 并发:

  1. 是在同一实体上的多个事件
  2. 是在一台处理器上"同时处理多个任务"
  3. 同一时刻,其实是只有一个事件在发生.

即多个线程抢占同一个资源.
在这里插入图片描述

1.2 并行

  1. 是在不同实体上的多个事件
  2. 是在多台处理器上同时处理多个任务
  3. 同一时刻,大家都在做事情.你做你的,我做我的.

即多个线程同时执行.

在这里插入图片描述

2. 进程,线程,管程

2.1 进程

在系统中运行的一个应用程序就是一个进程,每一个进程都有它自己的内存空间和系统资源.

2.2 线程

也被叫做轻量级进程,在同一个进程内会有1个或多个线程,是大多数操作系统进行时序调度的基本单元

2.3 管程

也被称为Monitor(监视器),也就是平时我们所说的锁.

3. 线程启动的三种方式

3.1 继承Thread类.重写run方法

继承重写

    public class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    }

执行

    public class ExtendThreadMain {
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.start();
        }
    }

注意:启动线程是调用父类的start方法,而不是直接去调用run方法.

3.2 实现Runable接口

实现重写

    public class MyThread implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    

主函数启动线程

    public class RunnableThreadMain {
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
    
            Thread t1 = new Thread(myThread);
    
            t1.start();
        }
    }
    

当然我们还有另外一种写法

注意到Thread的构造方法的参数有
在这里插入图片描述

里面存在一个Runnable参数和一个线程名的两个参数的方法.熟悉静态内部类或者Lamba表达式的应该了解,这里完全可以利用这种特性来启动线程,所以我们可以这样改写.


    public class RunnableThreadMain {
        public static void main(String[] args) {
    //        MyThread myThread = new MyThread();
    //        Thread t1 = new Thread(myThread);
    
            Thread t1 = new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + " " + i);
                }
            }, "t1");
    
            t1.start();
        }
    }
    

3.3 实现Callable接口

实现重写call方法(其实也就跟上述两个的run方法的作用一样)

    /**
     * 这里的泛型指的是call方法的返回值
     */
    public class MyThread implements Callable<Integer> {
    
        @Override
        public Integer call() throws Exception {
            int sum = 0;
            for (int i = 0; i < 100; i++) {
                sum+=i;
            }
            return sum;
        }
    }

主函数调度,这里与其他两种实现不同的是,可以有返回值,这里引用了FutureTask接口来接收这个值

    public class CallableThreadMain {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            MyThread myThread = new MyThread();
    
            FutureTask<Integer> futureTask = new FutureTask<>(myThread);
    
            Thread t1 = new Thread(futureTask);
    
            t1.start();
    
            Integer sum = futureTask.get();
            System.out.println("sum = " + sum);
        }
    }
    

注意:get方法时,方法会阻塞,直到这个线程执行完毕有返回值,代码才会往下进行.

4. 守护线程

守护线程

  • 守护线程是一种特殊的线程,它并不属于用户线程,也不受用户线程的控制,但它却有着特殊的重要性。
  • 守护线程的主要目的是为其他非守护线程提供服务。当所有的用户线程都结束时,守护线程也会随之结束
  • 守护线程的创建方式是在启动线程时调用Thread.setDaemon(true)方法,该方法将线程设置为守护线程。
  • 判断当前线程是否为守护线程调用 Thread.currentThread().isDaemon()方法,true为是,false为不是

守护线程的退出条件是:

  1. 所有用户线程都结束;
  2. 调用了Thread.stop()方法;
  3. 调用了Runtime.exit()方法。

注意:

  1. 守护线程只能有一个,一个JVM中只能有一个守护线程;
  2. 守护线程不能执行用户线程的run()方法,因为它没有用户线程可执行;
  3. 守护线程的优先级比较低,它只会在JVM中运行,不会影响到其他线程的运行;
  4. 守护线程的异常处理方式和非守护线程相同。
    public class ProtectThread {
        public static void main(String[] args) {
            //创建一个线程
            Thread t1 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + ": " +
                        (Thread.currentThread().isDaemon() ? " 守护线程" : "用户线程"));
                while (true) {
                }
            },"t1");
            t1.setDaemon(true);//设置为守护线程
            t1.start();
            
            System.out.println(Thread.currentThread().getName() + ": " +
                    (Thread.currentThread().isDaemon() ? " 守护线程" : "主线程"));
        }
    }

5. windows,linux下查看和杀死进程

5.1windows

  • 任务管理器可以查看进程和线程数,也可以用来杀死进程.

  • tasklist 查看进程
    在这里插入图片描述

  • taskkill 杀死进程

    • 通常带两个参数 /F 强制杀死 /PID 进程id

5.2 linux

  • ps -ef 查看所有进程
    在这里插入图片描述

  • ps -fT -p 查看某个进程(PID)的所有线程

在这里插入图片描述

  • kill 杀死进程
  • top按大写H切换是否显示线程

动态展示所有进程占用cpu的情况
在这里插入图片描述

  • top -H -p 查看某个进程(PID)的所有线程

5.3 查看Java的进程

  • jps 命令查看所有 Java进程

  • jstack 查看某个Java进程 (PID) 的所有线程状态.
    在这里插入图片描述

  • jconsole 来查看某个Java进程中线程的运行情况(图形界面)
    在这里插入图片描述

6. 线程运行原理

我们知道JVM虚拟机中有栈和堆两块内存区域.

而每一个线程都有属于自己的栈区,每个线程直接的栈互不干扰,线程中每一个方法都是一个栈帧.

看如下这段代码的调度过程:
在这里插入图片描述

我们再通过一幅图来理解

在这里插入图片描述

7. 线程的上下文切换

有些时候由于一些原因,当线程在执行时,cpu被其他线程抢占到也就是说存在线程之间的切换的过程,我们称为上下文切换.

哪些原因会导致线程上下文切换呢

  • 线程的cpu时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了sleep,yield,wait,join,park,synchronized,lock等方法

当上下文切换时,需要由操作系统去保存当前线程的状态,也就是记录其中的一些变量啊,执行到哪一步啦,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用是记住下一条JVM指令的执行地址,是线程自己私有的

  • 状态包括程序计数器,虚拟机栈中每个栈帧的信息,如局部变量,操作数栈,返回地址

注意:频繁的上下文切换会影响性能.

8. 线程的几种状态

8.1 五种(来源于操作系统)

在这里插入图片描述

  • 初始状态: 相当于我们刚刚new出来一个线程对象.仅是在语言层面创建了线程对象,还并未与操作系统线程相关联.
  • 可运行状态(就绪状态) : 指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行.也就是都准备去抢占cpu啦,如果抢到了就会进入运行状态
  • 运行状态: 指获取了CPU时间片运行中的状态.
    • 当CPU时间片用完会从运行状态转换至就绪状态, 会导致线程的上下文切换
  • 阻塞状态
    • 如果调用了阻塞的API,例如BIO(同步阻塞IO)读写文件,这时该线程不会用到CPU,会导致线程上下文切换,进入阻塞状态.
    • 等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态.
    • 与可运行状态的区别是,对阻塞状态的线程来说只要它们一直不唤醒,调度器一直不会考虑调度它们.

可以这样理解: 好比去上厕所,你抢到厕所的使用权啦,但是突然你又不想上啦,想起还有其他事情要做,总不能占着茅坑不拉屎叭,所以cpu,也就是厕所,你得离开这里,这个时候你去干其他的事都可以,等你忙完又想上厕所啦,这个时候又和别人一样需要有机会才能抢到厕所的使用权,也就是重新回到就绪状态.

  • 终止状态: 表示线程已经执行完毕,生命周期已经结束,不会在转换为其他状态.也就是代码执行完毕,没有循环的包裹,执行完就会进入终止状态.

8.2 六种(Java层面)

我们来查看Thread类当中的一个枚举类State
在这里插入图片描述
所以一共有六种状态,其具体为
在这里插入图片描述

  • NEW 线程刚被创建,但是还没有调用start()方法
  • RUNNABLE当调用了start()方法之后.

注意: JavaAPI层面的RUNNABLE状态涵盖了操作系统层面的可运行状态(就绪状态),运行状态,阻塞状态(由于BIO导致的线程阻塞,在Java里无法区别,仍然认为是可运行,也就是RUNNABLEZ状态)

  • BLOCKED 可以理解为抢占锁时未抢到,造成阻塞等待
  • WAITING 等待别的线程执行结束
  • TIMED_WATING 有时间的等待,也就是对应Thread.sleep();
  • TERMINATED 当线程代码运行结束
8.2.1 代码实现每种状态
  • NEW (创建线程不开始)
    在这里插入图片描述

  • RUNNABLE(就绪,运行,阻塞(操作系统的层面例如IO流时))
    在这里插入图片描述

  • TERMINATED (代码执行完毕就进入此状态)
    在这里插入图片描述

  • TIMED_WATING (睡眠时进入)
    在这里插入图片描述

  • WAITING(利用join方法,使线程2等待线程1执行完毕,此时状态为WAITING)
    在这里插入图片描述

  • BLOCKED(阻塞,抢占锁失败,阻塞等待)
    在这里插入图片描述

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值