深入学习JUC, 初识进程线程与实际使用,工作学习必备,看完必会。

在这里插入图片描述

什么是进程

  1. 一个程序由指令和数据组成, 程序运行过程中, 指令要加载进CPU , 数据要加载进内存 , 过程中还需要用到磁盘, 网络等等设备.
    进程就是用来加载指令, 管理内存, 管理IO的.
  2. 当一个程序被允许, 从磁盘当中加载这个程序的代码进内存, 这样就开启了一个进程.
  3. 进程可以看做是容器的一个实例. 大部分程序可以同时运行多个实例(例如记事本 , 画图, 浏览器等等), 有的程序只能启动一个实例进程, 比如网易云音乐等等.

什么是线程

  1. 一个进程内可以分为1到多个线程
  2. 一个线程就是一个指令流, 将指令流中的一条条指令以一定顺寻交给CPU执行
  3. Java中, 线程是最小调度单位,就是执行指令由一个个线程来完成, 进程是最小的资源分配单位.

线程与进程之间的区别

  • 进程基本上相互独立, 而线程存在于进程内, 是进程的一个子集
  • 进程拥有共享的资源, 如内存空间, 供其内部的线程共享
  • 进程间通信较为复杂
    同一台计算机之间的通信称为IPC(Inter-process communication)
    不同计算机之间的进程通信, 需要通过网络, 遵循共同的协议才能完成, 比如HTTP协议
  • 线程通信相对简单, 因为他们共享进程内的内存, 比如多个线程可以访问一个共享变量
  • 每个进程都有自己独立的地址空间, 进程之间的切换会有较大的开销; 而线程更加轻量级, 同一个进程内的线程共享进程的地址空间, 每个线程都要自己的独立的运行栈和程序计数器, 线程间切换开销小.
  • 进程是操作系统资源分配的基本单位, 线程是处理器任务执行的基本单位
  • 一个进程崩溃后, 在保护模式下其他进程不会被影响, 但是一个线程崩溃可能导致整个进程被操作系统杀掉, 所以多进程要比多线程健壮.

IPC协议

由于进程隔离的存在, 两个进程使用各自的虚拟地址空间, 每个进程只能访问自己虚拟地址空间.
而IPC则是进程会把内核开辟一块缓冲区, 进程把数据拷贝到内核缓冲区, 其他进程再把内核缓冲区的数据读取走.
这就是IPC机制, 进程间通信机制.

并发

单核CPU下, 线程是串行执行的, 操作系统的任务调度器将CPU的时间片分给不同的线程使用, 只是cpu在线程间的切换非常快, 人类的感觉是同时执行的, 就是微观下串行, 宏观上并行.

这种轮流使用通过时间片轮流使用CPU的做为称为并发 ,concurrent

并行

多核CPU下, 每个核都可以调度运行线程, 这就是真正的同一时刻运行多个线程

线程的应用(重点)

  1. 单核CPU下,多线程不能实际提高程序的运行效率, 甚至可能由于线程间平凡的上下文切换导致性能下降.
    但是仍然存在意义的, 不同线程轮流使用CPU, 不会导致一个线程一直阻塞下去.
  2. 多核CPU可以并行跑多个线程, 但是能否提高程序的运行效率还是要分情况的.
    有些复杂的任务可以通过拆分, 让几个线程并行完成, 就能提高运行效率.
    但是有些任务就是需要等待其他线程的结果, 那么此时多线程的存在也无实际意义.
  3. IO操作其实不占用CPU , 只是我们拷贝文件等等的IO操作一般使用的是阻塞IO , 这是虽然线程不使用CPU, 但是需要一直等待IO结束, 没 能充分利用线程 , 此时应该使用非阻塞IO和异步IO

Java线程之创建线程的4个方法

使用Thread 对象
使用Thread对象创建一个线程, 并且重写内部的run方法
run是线程具体需要执行的方法
start是将线程交给操作系统的任务调度器, 通过时间片真正的执行这个线程.
    public static void main(String[] args) {
        Thread t1=new Thread("线程1"){
            @Override
            public void run() {
                System.out.println("当前线程为"+Thread.currentThread().getName());
            }
        };
        t1.start();
    }
使用Runnable接口

Runnable是一个函数式接口, 即有且仅有一个抽象方法的接口.
通常函数式接口会加上一个@Functional注解,当你编写一个函数式接口时,如果语法错误,编译器会提示你, 且这个注解也只有这个功能.不加注解也不影响接口成为函数式接口, 只是如果语法错误不会提示.
只有使用函数式接口才能让lambda表达式正确推导

lambda表达式的使用是为了减少单个类或者匿名内部类的代码简洁度的.
同时, 匿名内部类会在打包时产生一个.class文件, 但是lambda就不会出现这种情况.
lambda对集合的迭代更加方便遍历.
配合stream流可以极大的提升开发效率.

通过Runnable接口将代码于线程分隔开, 使代码更加灵活, 比如编写好的一份run方法 , 可以在多个线程中进行复用.
Java中推荐组合优于继承 , 而Runnable与Thread就是组合与继承的关系, 使用Runable让任务类脱离了Thread的继承体系.

    public static void main(String[] args) {
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                System.out.println("111");
            }
        };
        Thread thread=new Thread(runnable);
        Thread thread1=new Thread(runnable);
        thread.start();
    }
使用Lambda简化Runable
只有一个参数时省略, 方法体只有一行代码时省略大括号
public static void main(String[] args) {
        Runnable runnable= () -> System.out.println("111");
        Thread thread=new Thread(runnable);
        Thread thread1=new Thread(runnable);
        thread.start();
    }

方法体多行代码时不能省略大括号
public static void main(String[] args) {
        Runnable runnable = () ->
        {
            System.out.println("111");
            System.out.println(222);
        };
        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable);
        thread.start();
    }
FutureTask创建线程
FutureTask间接继承了Runable, 特点是可以接收Callable类型的参数, 用来返回一个结果.
创建出的FutureTask类可以通过调用get()方法等待内部的run方法的执行结果, 这个等待是阻塞式等待.
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask=new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                Thread.sleep(2000);
                int count=0;
                for (int i = 0; i < 10; i++) {
                    count++;
                }
                return count;
            }
        });
        Thread thread=new Thread(futureTask,"线程1");
        thread.start();
        System.out.println(futureTask.get());    //得到结果
    }

查看进程情况

linux
ps -fe 查看所有进程
ps -fT -p <PID> 查看某个进程(PID)的所有线程
kill 杀死进程
top 按大写 H 切换是否显示线程
top -H -p <PID> 查看某个进程(PID)的所有线程
windows
任务管理器可以查看进程和线程数,也可以用来杀死进程
tasklist 查看进程
taskkill 杀死进程
jps 命令查看所有 Java 进程
jstack <PID> 查看某个 Java 进程(PID)的所有线程状态
jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木 木 水.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值