深入理解多线程

举报
酸菜鱼136 发表于 2022/11/09 11:08:16 2022/11/09
【摘要】 多线程相关知识

简单记录一下最近学习的多线程内容,用以日后复习所用。

一、何为线程和进程?

1.1 进程

进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
但进程存在着一定的缺陷:
1)进程同一时间只能做一件事
2)进程在执行过程中如果由于某种原因阻塞了,例如等待输入,整个进程就会挂起,其他与输入无关的操作也必须等待输入完成后才能顺序执行。
因此,引入了线程来解决上述问题。

1.2 线程

线程和进程相似,但线程是一个比进程更小的执行单位,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

总的来说:
进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。
线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。

1.3 进程和线程的区别与联系

1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可以识别的最小执行和调度单位
2)资源分配给进程,同一进程的所有线程贡献该进程的所有资源。同一进程的多个线程共享代码段(代码和常量)、数据段(全局变量和静态变量)、扩展段(堆存储)。但每个线程用于在建的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。即同一进程中的多个线程共享进程的方法区,但每个线程都有自己的程序计数器虚拟机栈本地方法栈
jdk1.8前
在这里插入图片描述
jdk1.8后
在这里插入图片描述

3)线程是进程划分的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而个线程则不一定,因为同一进程中的线程即可能会相互影响。线程执行开销小,但不利于资源的管理和保护,而进程则相反。

补充:

1、程序计数器:程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
另外,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
2、Java虚拟机栈:虚拟机栈也称为Java栈,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)。其中栈帧包括局部变量表、操作数栈、动态链接、方法返回地址和一些附加信息。
每一个方法被调用直至执行完毕的过程,就对应这一个栈帧在虚拟机栈中从入栈到出栈的过程。(详解看:https://blog.csdn.net/u012988901/article/details/100043857)
3、本地方法栈:和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
4、堆:Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
5、方法区:方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

补充:

并发:同一时间段,多个任务都在执行(单位时间内不一定同时执行)
并行:单位时间内,多个任务同时执行。

二、多线程

2.1 为什么要使用多线程?

1)更多的处理器核心
一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心加入也无法显著该程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。
2)更快的响应时间
可以使用多线程技术,将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列)。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短响应时间,提升用户体验。

例如:用户提交一次订单,后续会等待一系列操作全部完成才能看到订购成功的结果。对于其中的一些业务操作,可以考虑交给其他线程处理,从而使其更快地完成。
3)更好的编程模型
Java为多线程编程提供了良好、考究并且一致的编程模型,当开发人员为所遇到的问题建立合适的模型之后,稍作修改就能够方便地映射到Java提供的多线程编程模型上。

2.2 线程的状态

5种线程状态,在任意一个时间点,一个线程只能有且只有其中一个状态。
1)新建(new):初始状态,线程呗创建,但还没调用start方法;
2)运行(Runable):运行状态,包括了操作系统线程状态的Running和Ready,处于此状态的线程可能正在执行,也可能正在等待着cpu为其分配执行时间;
3)等待(Wating):线程进入等待状态,处于此状态的线程不会被分配cpu执行时间。等待状态又分为无限等待和有限期等待;
4)阻塞(Blocked):线程被阻塞了,“阻塞状态”与”等待状态“的区别是:”阻塞状态“在等待着获取到一个排他锁,这个时间将在另外一个线程放弃这个锁的时候发生;而”等待状态“则是在等待一段时间或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态
5)终止(Terminated):线程执行完毕。
在这里插入图片描述

2.3 线程的创建方法

创建线程共有四种方法:继承Thread类、实现Runnable接口、实现Callable接口和线程池。下面将详细介绍:
1)继承Thread类

//继承Thread类来创建线程
public class ThreadTest {
 
    public static void main(String[] args) {
        //设置线程名字
        Thread.currentThread().setName("main thread");
        MyThread myThread = new MyThread();
        myThread.setName("子线程:");
        //开启线程
        myThread.start();
        for(int i = 0;i<5;i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}
 
class MyThread extends Thread{
    //重写run()方法
    public void run(){
        for(int i = 0;i < 10; i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

2)实现Runable接口

//实现Runnable接口
public class RunnableTest {
  
    public static void main(String[] args) {
        //设置线程名字
        Thread.currentThread().setName("main thread:");
        Thread thread = new Thread(new MyRunnable());
        thread.setName("子线程:");
        //开启线程
        thread.start();
        for(int i = 0; i <5;i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}
  
class MyRunnable implements Runnable {
  
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

3)实现Callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现Callable接口
public class CallableTest {
 
    public static void main(String[] args) {
        //执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
        new Thread(futureTask).start();
        //接收线程运算后的结果
        try {
            Integer sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
 
class MyCallable implements Callable<Integer> {
 
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}

4)线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//线程池实现
public class ThreadPoolExecutorTest {
 
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ThreadPool threadPool = new ThreadPool();
        for(int i =0;i<5;i++){
            //为线程池分配任务
            executorService.submit(threadPool);
        }
        //关闭线程池
        executorService.shutdown();
    }
}
 
class ThreadPool implements Runnable {
 
    @Override
    public void run() {
        for(int i = 0 ;i<10;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }

}

补充
Runnable不会返回结果或者抛出异常,Callable接口可以。
Thread: 继承方式, 不建议使用, 因为Java是单继承的,继承了Thread就没办法继承其它类了,不够灵活

三、多线程中存在的问题

多线程中会存在线程安全问题、活跃性问题、性能问题,详解可以看下面这篇文章:http://www.360doc.com/content/21/0415/23/58006001_972536715.shtml

附:java多线程问题总结
参考:https://www.cnbl ogs.com/coder-programming/p/10595804.html
《可能是把 Java 内存区域讲的最清楚的一篇文章》.
https://www.cnblogs.com/Aug-20/p/11808138.html
https://blog.csdn.net/vbirdbest/article/details/81282163
https://www.cnblogs.com/zhou-test/p/9811771.html
https://snailclimb.gitee.io/javaguide/#/

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。