Java多线程方面知识

news2024/11/24 21:03:56

目录

1.程序、进程、线程

2.进程与线程的内存解析

3.并发与并行

4.Java程序的线程

5.多线程的创建:方式一:继承于Thread类

6.start方法的作用

7.使用start()时要注意的问题

8.Thread类中的一些方法

9.线程的优先级

10.多线程的创建:方式二:实现Runnable

11.比较创建线程的两种方式

1.开发中,优先选择:实现Runnable接口方式

2.二者的联系

12.线程的生命周期

13.线程的同步:解决线程安全问题

14.同步的方式:方式一:同步代码块

15.同步的方式二:同步方法:

16.同步方法的总结

17.同步机制的优缺点

18.单例模式的懒汉式线程安全

19.死锁

20.线程同步的方法三:Lock锁

21.面试题:synchronized和lock的异同

22.优先使用的同步方法顺序:

23.面试题:解决线程安全的方式

24.线程的通信(应用:生产者消费者问题)

25.说明:wait、notify、notifyall的注意事项

26.面试题:sleep()和wait()的异同?

27.线程创建方式三:实现Callable接口

28.为什么Callable接口比实现Runnable接口创建多线程更强大

29.线程创建方式四:使用线程池方式

30.线程池的好处

31.面试题:创建多线程有哪几种方式?


1.程序、进程、线程

1.程序(program):完成特定任务、用某种语言编写的一组指令的集合

2.进程(process):正在运行的一个程序。进程是资源分配的单位。

3.线程(thread):一个程序内部的一条执行路径。线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器。

2.进程与线程的内存解析

1.一个进程公用一个堆和方法区,每个线程独立的拥有一个程序计数器和虚拟机栈

2.因此一个进程中的多个线程可以共享堆中的对象,使线程通信更简单高效,但也因此带来安全隐患。

3.并发与并行

并行:多个CPU可以执行多个任务

并发:一个CPU(采用时间片)同时执行多个任务。

4.Java程序的线程

一个java程序java.exe,至少三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。

5.多线程的创建:方式一:继承于Thread类

        1.创建一个继承于thread类的子类。

        2.重写thread类的run()

        3.创建thread类的子类的对象

        4.通过此对象调用start()

public class MyThreadTest {
 
    public static void main(String[] args) {
        //3.创建Thread类的子类的对象
        Thread1 thread1 = new Thread1();
        //4.通过该对象调用start()方法
        thread1.start();
    }
 
}
//1.创建一个继承了Thread类的子类
class Thread1 extends Thread {
    //2.重写Thread类的run()方法
    @Override
    public void run() {
        System.out.println("线程实现方式一:继承Thread类方式");
    }
}

6.start方法的作用

        启动当前线程;调用当前线程的run()。

7.使用start()时要注意的问题

问题一:我们不能通过直接调用run()的方式启动线程,这样相当于在main中启动。

问题二:不能够让已经start()的线程在执行一次start,会报IllegalThreadException。只能重新创建一个线程对象来start。

8.Thread类中的一些方法

        1.start():启动当前线程;调用当前线程的run()

        2.run():通常需要重写thread类中的此方法,将创建的线程要执行的操作声明在此方法中

        3.currentThread():静态方法,返回当前代码的线程

        4.getName():获取当前线程的名字

        5.setName():设置当前线程的名字

        6.yield():释放当前CPU的执行权

        7.join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到线程b执行完毕后线程a才结束阻塞状态。

        8.stop():已过时。当执行此方法时,强制结束当前线程。

        9.sleep(long millitime):让当前线程睡眠指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。

        10.isAlive():判断当前线程是否存活。

9.线程的优先级

        1.MAX_PRIORITY :最大优先级:10

        2.MIN_PRIORITY :最小优先级:1

        3.NORM_PRIORITY :5;默认优先级

        4.如何获取和设置当前线程优先级

                getPriority():获取线程的优先级

                setPriority(int p):设置当前线程的优先级

        5.高优先级的线程抢占低优先级线程CPU的执行权。但只是从概率上讲,高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

10.多线程的创建:方式二:实现Runnable

        1.创建一个实现了Runnable接口的类

        2.实现类去实现Runnable中的抽象方法run

        3.创建实现类的对象

        4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象

        5.通过Thread类的对象调用start()

public class MyThreadTest {
 
    public static void main(String[] args) {
        //3.创建该实现类的对象
        Thread2 thread2 = new Thread2();
        //4.将该对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread thread3 = new Thread(thread2);
        //5.通过Thread类的对象调用start()方法
        thread3.start();
    }
}
//1.创建一个实现了Runnable接口的实现类
class Thread2 implements Runnable{
    //2.重写run()方法
    @Override
    public void run() {
        System.out.println("线程实现方式二:实现Runnable接口方式");
    }
}

11.比较创建线程的两种方式

1.开发中,优先选择:实现Runnable接口方式

        原因:

                1.实现的方式没有类的单继承性的局限性。

                2.实现的方式更适合来处理多个线程有共享数据的情况。

2.二者的联系

        联系:Thread类也继承了Runnable

        相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

                      两种方式想要启动线程,都是要调用Thread类中的start()。

12.线程的生命周期

 

13.线程的同步:解决线程安全问题

1.线程的安全问题:冲票、错票问题

 

2.问题出现的原因:某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来也进行该操作引起的。(使用了共享数据)

3.如何解决:当一个线程在操作共享数据时,其他线程无法参与,直到该线程操作完毕其他线程才可以参与。即使该线程出现阻塞也不能改变。

4.java中通过同步机制来解决线程安全问题

5.共享数据:多个线程共同操作的变量

14.同步的方式:方式一:同步代码块

1.同步代码块:实现Runnable的形式

synchronized(同步监视器){需要被同步代码}

2.说明:操作共享数据的代码,即为需要被同步的代码。

3.同步监视器,俗称锁;任何类的对象都可以充当锁;

4.要求:多个线程必须共用同一把锁

5.在实现Runnable接口创建多线程的方式中,可以考虑使用this作为锁;

6.在继承Thread类创建多线程的方式中,慎用this充当锁,可以考虑使用当前类充当同步监视器

class Window1 implements Runnable{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while(true){
            //使用类的对象充当同步监视器
//            synchronized(obj){
            //使用当前类充当同步监视器
//            synchronized(Window1.class){
            //使用this充当同步监视器
            synchronized(this){
                if (ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":购票成功,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}

7.注意:synchronized不能包多了也不能包少了,包多了会变成单线程执行,包少了就没有实现线程安全。

15.同步的方式二:同步方法:

1.如果操作共享数据的代码完整的声明在一个方法中,我们可以将此方法声明为同步

2.需要将共享数据和同步方法写成静态形式

class Window2 implements Runnable{
    private static int ticket = 100;
    @Override
    public void run() {
        while(true){
         getTicket();
        }
    }
    private static synchronized void getTicket(){//默认是一个this
        if (ticket > 0){

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":购票成功,票号为:" + ticket);
            ticket--;
        }
    }

16.同步方法的总结

1.同步方法虽然仍然涉及到同步监视器,只是不需要我们显式的声明。

2.非静态的同步方法,同步监视器是this

   静态的同步方法,同步监视器是:当前类本身

17.同步机制的优缺点

好处:同步的方式,解决了线程的安全问题

局限性:操作同步代码时只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率会变低。

18.单例模式的懒汉式线程安全

public class Lan {
    private Lan(){};

    private static Lan lan = null;

    public static Lan getLan(){
        synchronized(Lan.class) {
            if (lan == null) {
                lan = new Lan();
            }
        }
        return lan;
    }
}

19.死锁

1.死锁概念:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,形成了线程的死锁。

2.说明:所有线程都处于阻塞状态,但不会出现异常。平时要避免出现死锁。

20.线程同步的方法三:Lock锁

1.先使用reentrant lock实例化

2.在try中调用锁定方法:lock()

3.在finally中调用解锁方法:unlock()

class Window implements Runnable{
    private int ticket = 100;
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try {
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":购票成功,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }finally{
                lock.unlock();
            }
        }
    }
}

21.面试题:synchronized和lock的异同

相同点:都是用来解决线程安全问题的;

不同点:

        synchronized:在执行完相应的同步代码后,自动地释放同步监视器

        lock:手动的启动同步(lock()),同时结束时也需要手动的实现(unlock())。

22.优先使用的同步方法顺序:

        1.lock

        2.同步代码块(已经进入了方法体,分配了相应资源)

        3.同步方法(将整个方法全置为单线程)

23.面试题:解决线程安全的方式

        同步代码块,同步方法,lock锁

24.线程的通信(应用:生产者消费者问题)

1.调用wait()方法的线程进入阻塞状态,一旦wait就会释放所有同步监视器

2.使用notify()和notifyall()唤醒wait的线程

3.notify()唤醒一个被wait的线程,若有多个线程被wait,则唤醒优先级高的那个

4.使用notifyall会唤醒所有被wait的线程

25.说明:wait、notify、notifyall的注意事项

1.wait、notify、notifyall三个方法必须使用在同步代码块或同步方法中

2.wait、notify、notifyall的调用者(要么全是this,要么全是自定义的任意对象)必须是同步代码块或者同步方法中的同步监视器,否则会出现异常。

3.wait、notify、notifyall三个方法是调用在object类中的

26.面试题:sleep()和wait()的异同?

相同点:都会使得当前线程进入阻塞状态

不同点:

1.sleep到时间后会主动进入就绪,wait方法必须调用notify唤醒

2.方法声明的位置不同:thread类中声明sleep(),object类中声明wait()

3.调用的要求不同:sleep()方法可以在任何需要的场景下调用,wait()必须使用在同步代码块或同步方法中

4.是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会主动释放锁。

27.线程创建方式三:实现Callable接口

1.创建一个实现Callable的实现类

2.实现call()方法,将此线程需要执行的方法声明在call()中

3.创建callable实现类的对象

4.将此实现类对象作为参数传递到FutureTask构造器中,创建FutureTask对象

5.将FutureTask对象作为参数传递到Thread类中调用start()

6.可以获取Callable中call方法的返回值

class CallableTest implements Callable {

    @Override
    public Object call() throws Exception {
        int sum = 0;
            for (int i = 0; i <= 100; i++) {
                    if (i % 2 == 0){
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                        sum += i;
                    }
            }
        return sum;
        }


}
public class Demo1 {
    public static void main(String[] args) {
        CallableTest c = new CallableTest();
        FutureTask f = new FutureTask(c);
        Thread t1 = new Thread(f);
        Thread t2 = new Thread(f);
        t1.setName("线程1");
        t2.setName("线程2");
        t2.start();
        t1.start();
        try {
            Object sum = f.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

28.为什么Callable接口比实现Runnable接口创建多线程更强大

1.call()可以有返回值

2.call()可以抛出异常,被外面的操作捕获,获取异常的信息

3.Callable是支持泛型的

29.线程创建方式四:使用线程池方式

1.提供指定线程数量的线程池

2.执行指定的线程的操作;需要提供实现Runnable接口或Callable接口实现类的对象

3.关闭连接池

class A implements Runnable {
    @Override
    public void run() {

        System.out.println("a");
    }
}

public class Test {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new A());
        service.shutdown();
        String s = "abc";
    }
}

30.线程池的好处

1.提高响应速度(减少了创建新线程的时间)

2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)

3.便于线程管理。

31.面试题:创建多线程有哪几种方式?

继承Thread类、实现Runnable接口、实现Callable接口、线程池

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/599597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JAVA】清缓存(打断点删除 / 新增表或字段无效)

一. 打断点无效 情景 新写了一个获取列表数据接口&#xff0c;前端调用的&#xff0c;获取到的数据为空数组。在数据库中查看&#xff0c;是有数据的&#xff0c;但是调用接口返回数据为空。接下来就打断点啦&#xff0c;发现无效&#xff01;表现如下 没有可执行的…

【Bio】牙的分类

文章目录 IncisorCentral IncisorLateral Incisor Canine / CuspidPremolarFirst PremolarSecond Premolar MolarFirst MolarSecond MolarThird Molar / Wisdom Tooth Ref Incisor Central Incisor Lateral Incisor Canine / Cuspid Premolar First Premolar Second Prem…

第六十二天学习记录:C语言进阶:C语言预处理2

带副作用的宏参数 当宏参数在宏的定义中出现超过一次的时候&#xff0c;如果参数带有副作用&#xff0c;那么你在使用这个宏的时候就可能出现危险&#xff0c;导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。例如&#xff1a; x1;//不带副作用 x;//带有副作…

安全认证:

1. 认证概述 为什么要有认证&#xff1f; 防止非法路由器接入企业内网的ospf路由器&#xff0c;保护内网安全 2. 认证方式 认证方式分为接口认证和区域认证&#xff0c;接口认证和区域认证没有本质的区别&#xff0c;接口认证是当区域内链路过多的情况下&#xff0c;接口认证…

Java网络开发(Tomcat)—— Servlet学习 Web相关背景知识 JavaWeb项目初步

本文目录 引出一、软件架构BS和CS二、实现Web服务的条件和步骤三、Tomcat搭建Web项目初步1.pom.xml文件配置2.web.xml文件更新3.Tomcat运行环境配置4.项目文件层级解析 四、JavaWeb项目文件分类&#xff08;1&#xff09;静态文件—存放位置&#xff08;2&#xff09;动态文件-…

【用人话讲算法】leetcode无重复字符的最长子串

【用人话讲算法】leetcode无重复字符的最长子串 文章目录 【用人话讲算法】leetcode无重复字符的最长子串题目简单思路&#xff08;暴力&#xff09;优化思考怎么写代码&#xff1f;怎么到下一个&#xff1f;whilefor 思路总结while和for循环总结 题目 题目的意思是说&#xff…

一台电脑上安装多个版本的python,运行互不干扰,显示位置的一些命令,

首先需要知道一些命令&#xff1a; pip show 包名 可以使用pip show 包名的方式显示位置 pip list pip方式显示的是当前环境下的库 os.environ.get&#xff08;&#xff09; python中os模块获取环境变量的一个方法 Python os.environ.get&#xff08;&#xff09;的用法 …

使用Git LFS上传Unity大型资源文件

在使用Unity制作结课作业时&#xff0c;使用到git工具进行版本控制与多人协作。在提交项目至远程仓库的过程中&#xff0c;git bash提示了以下报错&#xff1a; remote: warning: File Assets/Models/Z_India_5.29.fbx is 57.31 MB; this is larger than GitHubs recommended m…

利用环回口建立IBGP邻居

利用环回口建立IBGP邻居 BGP的稳定性 IBGP : 1.一般使用环回口建立IBGP邻居 2.指定建立IBGP邻居的源地址为lookback地址 EBGP : 1.一般使用直连接口建立EBGP邻居关系 2.如果想使用环回口建立EBGP邻居&#xff0c;需要将TTL值修改为大于1&#xff0c;默认TTL1 修改命令…

在win10/11的右键菜单添加电源选项

前言&#xff1a; 今天&#xff0c;博主从网上找到了在右键菜单中添加电源选项的方法&#xff0c;觉得挺实用的所以来教大家 方法&#xff1a; 下载&#xff08;懒人专用&#xff0c;直接打开文件即可&#xff09;&#xff1a; csdn中下载&#xff08;启用和关闭文件都有&a…

基于 Docker_redis6.0.8 实现 Redis 集群扩缩容

文章目录 单机部署数据分区方案集群部署集群容错测试集群扩容测试集群缩容测试 LXL&#xff1a;这玩意太枯燥&#xff0c;看完需要耐心 ~~~ 单机部署 通过 dockerhub 查找 redis 镜像&#xff0c;选择 6.0.8 版本。创建挂载目录&#xff0c;并赋予权限。 mkdir -p /var/docker…

MicroPython应用基础-使用Thonny IDE

MicroPython应用基础-使用Thonny IDE 文章目录 MicroPython应用基础-使用Thonny IDE引言Thonny简介使用Thonny连接到MicroPython开发板使用Thonny的REPL窗口运行Python语句在Thonny中保存Python程序文件至MicroPython开发板中运行使用Thonny的注意要点 引言 在很长一段时间内&…

[论文分享]TimeMAE:解耦掩码自编码器时间序列的自监督表示

论文题目&#xff1a;TimeMAE: Self-Supervised Representations of Time Series with Decoupled Masked Autoencoders 论文地址&#xff1a;https://arxiv.org/abs/2303.00320 代码地址&#xff1a;https://github.com/Mingyue-Cheng/TimeMAE 1 摘要 利用自监督预训练增强基于…

Flume和Kafka的组合使用

一.安装Kafka 1.1下载安装包 通过百度网盘分享的文件&#xff1a;复制链接打开「百度网盘APP 即可获取」 链接&#xff1a;https://pan.baidu.com/s/1vC6Di3Pml6k1KMbnK0OE1Q?pwdhuan 提取码&#xff1a;huan 也可以访问官网&#xff0c;下载kafka2.4.0的安装文件 1.2解…

Hadoop3.1.3安装(单机、伪分布)

系列文章目录 Ubuntu常见基本问题 Hadoop3.1.3安装&#xff08;单机、伪分布&#xff09; Hadoop集群搭建 文章目录 系列文章目录一、环境1、创建hadoop用户 二、更新apt三、安装SSH、配置SSH无密码登陆四、安装Java环境五、安装 Hadoop3.1.3六、Hadoop单机配置(非分布式)七、…

chatgpt赋能python:Python为什么被吹得这么神?

Python为什么被吹得这么神&#xff1f; Python是一个开源、跨平台的高级编程语言&#xff0c;由Guido van Rossum于1989年在荷兰创造。Python在近几年因其方便易用、高效稳定和丰富的生态体系而备受欢迎。Python被广泛应用于数据科学、人工智能、机器学习、Web开发、自动化测试…

启动虚拟机并安装Linux系统

我们刚刚新建的虚拟机相当于一个裸机&#xff0c;还没有安装操作系统在里面&#xff0c;下面我们来看一下怎么进行Linux系统的安装。 在VMware Workstation工具的主界面选择虚拟机Spark01&#xff0c;单击鼠标右键在弹出的菜单中选择“设置”打开“虚拟机设置”对话框。如下图…

程序设计综合实习(C语言):学生成绩文件管理

一、目的 1&#xff0e;掌握文件指针的概念和运用 2&#xff0e;掌握文件的相关操作&#xff1a;打开、读、写、关闭 3&#xff0e;掌握文件的定位操作 二、实习环境 Visual Stdio 2022 三、实习内容与步骤 1&#xff0e;定义一个结构体数组&#xff0c;存放10个学生的学号&a…

ABB Drive Composer Pro 2.8.1 Crack

Drive Composer 是 ABB 通用架构驱动器的启动和维护工具。该工具用于查看和设置驱动器参数&#xff0c;以及监控和调整过程性能。 Drive Composer入门版提供了设置参数、基本监控、从 PC 对驱动器进行本地控制以及事件记录器处理等基本功能。 Drive Composer pro是成熟的调试和…

蓝桥杯ABC组 数论知识合集

Note Of Note 同余方程中是可以正常进行分数的约分和去分母的 e x g c d exgcd exgcd 在使用时要保证 a , b a,b a,b 都是非负数 a a a 与 b b b 互质不代表 a , b a,b a,b 都为质数&#xff08; 4 4 4 和 5 5 5 互质&#xff0c;但是 4 4 4 不是质数&#xff09;两个相邻…