多线程——学习笔记 1

目录

        • 多线程的了解
        • 多线程并行和并发的区别
        • Java程序运行原理
        • 多线程程序实现的方式
          • 1.继承Thread
          • 2.实现Runnable
        • 多线程(实现Runnable的原理)
        • 实现多线程两种方式的区别
        • 匿名内部类实现线程的两种方式
        • 获取线程名字和设置名字
        • 获取当前线程的对象——hread.currentThread()
        • 休眠线程——Thread.sleep
        • 守护线程——setDaemon
        • 加入线程——join
        • 礼让线程——yield
        • 设置线程的优先级——setPriority
        • 同步代码块
        • 线程安全问题
        • 死锁
        • 线程安全

多线程的了解

  • 什么是线程

    • 线程是程序执行的一条路径, 一个进程中可以包含多条线程
    • 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
  • 多线程的应用场景

    • 红蜘蛛同时共享屏幕给多个电脑
    • 迅雷开启多条线程一起下载
    • QQ同时和多个人一起视频
    • 服务器同时处理多个客户端请求

    多线程并行和并发的区别

  • 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)

  • 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。

  • 比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。

  • 如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。

Java程序运行原理

  • Java程序运行原理

    • Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
  • JVM的启动是多线程的吗

    • JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

多线程程序实现的方式

1.继承Thread
  • 定义类继承Thread
  • 重写run方法
  • 把新线程要做的事写在run方法中
  • 创建线程对象
  • 开启新线程, 内部会自动执行run方法
public class Demo2_Thread {public static void main(String[] args) {MyThread mt = new MyThread();							//4,创建自定义类的对象mt.start();												//5,开启线程for(int i = 0; i < 3000; i++) {System.out.println("bb");}}}
class MyThread extends Thread {									//1,定义类继承Threadpublic void run() {											//2,重写run方法for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中System.out.println("aaaaaaaaaaaaaaa");}}
}
2.实现Runnable
  • 定义类实现Runnable接口
  • 实现run方法
  • 把新线程要做的事写在run方法中
  • 创建自定义的Runnable的子类对象
  • 创建Thread对象, 传入Runnable
  • 调用start()开启新线程, 内部会自动调用Runnable的run()方法
public class Demo3_Runnable {public static void main(String[] args) {MyRunnable mr = new MyRunnable();						//4,创建自定义类对象Thread t = new Thread(mr);								//5,将其当作参数传递给Thread的构造函数t.start();												//6,开启线程
for(int i = 0; i < 3000; i++) {System.out.println("bb");}}
}class MyRunnable implements Runnable {							//1,自定义类实现Runnable接口@Overridepublic void run() {											//2,重写run方法for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中System.out.println("aaaaaaaaaaaaaaaaaa");}}}     		

多线程(实现Runnable的原理)

  • 1、看Thread类的构造函数,传递了Runnable接口的引用
  • 2、通过init()方法找到传递的target给成员变量的target赋值
  • 3、查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

实现多线程两种方式的区别

  • 查看源码的区别:

  • a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法

  • b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法

  • 继承Thread

  • 好处是:可以直接使用Thread类中的方法,代码简单

  • 弊端是:如果已经有了父类,就不能用这种方法

  • 实现Runnable接口

  • 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的

  • 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

    匿名内部类实现线程的两种方式

  • 继承Thread类

 new Thread() {                                                               //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 1000; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("saaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");}}}.start();                                                                   //4.开启线程
  • 实现Runnable接口
        new Thread(new Runnable() {                                                  //1.将Runnable的子类对象传递给Thread@Overridepublic void run() {                                                      //2.重写run方法for (int i = 0; i < 1000; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("bbbbbbbbbbb");}}}).start();                                                                  //4.开启线程

获取线程名字和设置名字

  • 通过getName()方法获取线程对象的名字
new Thread() {                                                               //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 1000; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("线程名为:"+getName());}}}.start();                                                                   //4.开启线程

在这里插入图片描述

  • 通过构造函数可以传入String类型的名字
new Thread("主线程1") {                                                               //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 1000; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("线程名为:"+getName());}}}.start();                                                                   //4.开启线程

在这里插入图片描述

  • 通过setName(String)方法可以设置线程对象的名字
Thread t1 = new Thread() {                                                   //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 1000; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("线程名为:"+getName());}}};                                                                          //4.开启线程t1.setName("设置线程名1");t1.start();

在这里插入图片描述

获取当前线程的对象——hread.currentThread()

  • hread.currentThread(),获取当前线程的对象,主线程也可以获取
Thread.currentThread().setName("1001");                                        //获取主函数线程的引用,并改名字System.out.println(Thread.currentThread().getName());                      //获取主函数线程的引用,并获取名字new Thread(new Runnable() {                                                  //1.将Runnable的子类对象传递给Thread@Overridepublic void run() {                                                      //2.重写run方法for (int i = 0; i < 10; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("bbbbbbbbbbb");}}}).start();                                                                //4.开启线程

在这里插入图片描述

休眠线程——Thread.sleep

    • Thread.sleep(毫秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒
  • Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 1000000000
 new Thread(new Runnable() {                                                  //1.将Runnable的子类对象传递给Thread@Overridepublic void run() {                                                      //2.重写run方法for (int i = 0; i < 10; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("倒计时"+i+"秒");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();                                                                //4.开启线程

在这里插入图片描述

守护线程——setDaemon

  • setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
public static void main(String[] args) {Thread t1 = new Thread() {                                                   //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 2; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("线程名为:" + getName());}}};                                                                          //4.开启线程Thread t2 = new Thread() {                                                  //1.将Runnable的子类对象传递给Thread@Overridepublic void run() {                                                      //2.重写run方法for (int i = 0; i < 5; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("倒计时"+i+"秒");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};                                                                //4.开启线程t2.setDaemon(true);                                                //将t2设置为守护线程t1.start();t2.start();}
}

在这里插入图片描述
有时间缓冲,所以非守护线程有时也会执行

加入线程——join

  • join(),当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
  • join(int), 可以等待指定的毫秒之后继续
 public static void main(String[] args) {final Thread t1 = new Thread() {                                                   //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 5; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("线程名为:" + getName());}}};                                                                          //4.开启线程Thread t2 = new Thread() {                                                  //1.将Runnable的子类对象传递给Thread@Overridepublic void run() {                                                      //2.重写run方法for (int i = 0; i < 5; i++) {                                     //3.将要执行的代码写在run方法中if (i==2){try {// t1.join();                                                //匿名内部类在使用它所在方法中的局部变量时必须用final修饰t1.join(1);                                           //插队指定时间,在指定时间执行完后,两条线程继续交替执行} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("倒计时"+i+"秒");}}};                                                                //4.开启线程t1.start();t2.start();}

在这里插入图片描述

礼让线程——yield

  • yield让出cpu
public static void main(String[] args) {new MyThread().start();new MyThread().start();                                  }
}class MyThread extends Thread{public void run(){for (int i = 0 ;i<1000;i++){if (i % 10 ==0){Thread.yield();}System.out.println(getName()+"线程"+i);}}}

在这里插入图片描述

设置线程的优先级——setPriority

  • setPriority()设置线程的优先级
public static void main(String[] args) {Thread t1 = new Thread() {                                                   //1.继承Thread类public void run() {                                                      //2.重写run方法for (int i = 0; i < 100; i++) {                                     //3.将要执行的代码写在run方法中System.out.println("线程名为:" + getName()+"____"+i);}}};                                                                          //4.开启线程Thread t2 = new Thread() {                                                  //1.将Runnable的子类对象传递给Thread@Overridepublic void run() {                                                      //2.重写run方法for (int i = 0; i < 100; i++) {                                     //3.将要执行的代码写在run方法中System.out.println(getName()+"倒计时" + i + "秒");}}};                                                                //4.开启线程t1.setPriority(Thread.MIN_PRIORITY);//设置最小优先级t2.setPriority(Thread.MAX_PRIORITY);//设置最大优先级t1.start();t2.start();}
}

在这里插入图片描述

同步代码块

  • 1.什么情况下需要同步
    • 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
    • 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
  • 2.同步代码块
    • 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
    • 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
public class Synchronized {public static void main(String[] agr) {final printer p = new printer();new Thread() {public void run() {while (true) {p.print1();}}}.start();new Thread() {public void run() {while (true) {p.print2();}}}.start();}
}class printer {public void print1() {demo d = new demo();synchronized (d) {   //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象System.out.print("北");System.out.print("京");System.out.print("欢");System.out.print("迎");System.out.print("您");System.out.print("\r\n");}}/** 非静态同步函数的锁是:this* 静态的同步函数的锁是:字节码对象*/public void print2() {demo d = new demo();synchronized (d) {System.out.print("武");System.out.print("汉");System.out.print("热");System.out.print("干");System.out.print("面");System.out.print("\r\n");}}
}class demo {}

在这里插入图片描述

  • 使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
public class Synchronized {public static void main(String[] agr) {final printer p = new printer();new Thread() {public void run() {while (true) {p.print1();}}}.start();new Thread() {public void run() {while (true) {p.print2();}}}.start();}
}/*** 非静态的同步方法的锁对象是this* 静态的同步方法的锁对象是该类的字节码对象*/
class printer {public synchronized void print1() {//锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象//使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的System.out.print("北");System.out.print("京");System.out.print("欢");System.out.print("迎");System.out.print("您");System.out.print("\r\n");}/** 非静态同步函数的锁是:this* 静态的同步函数的锁是:字节码对象*/public void print2() {synchronized (this) {System.out.print("武");System.out.print("汉");System.out.print("热");System.out.print("干");System.out.print("面");System.out.print("\r\n");}}
}

在这里插入图片描述

线程安全问题

  • 多线程并发操作同一数据时, 就有可能出现线程安全问题
  • 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
public class Demo2_Synchronized {public static void main(String[] args) {TicketsSeller t1 = new TicketsSeller();TicketsSeller t2 = new TicketsSeller();TicketsSeller t3 = new TicketsSeller();TicketsSeller t4 = new TicketsSeller();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t4.setName("窗口4");t1.start();t2.start();t3.start();t4.start();}}class TicketsSeller extends Thread {private static int tickets = 100;static Object obj = new Object();public TicketsSeller() {super();}public TicketsSeller(String name) {super(name);}public void run() {while(true) {synchronized(obj) {if(tickets <= 0)break;try {Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() + "...这是第" + tickets-- + "号票");}}}
}

在这里插入图片描述

多次启动一个线程是非法的

public class Demo2_Synchronized {public static void main(String[] args) {TicketsSeller ticketsSeller = new TicketsSeller();/* Thread t1 = new Thread(ticketsSeller);//多次启动一个线程是非法的t1.start();t1.start();t1.start();t1.start();*/}
}class TicketsSeller implements Runnable {private static int tickets = 100;public void run() {while(true) {synchronized(TicketsSeller.class) {if(tickets <= 0)break;try {Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");}}}
}

死锁

  • 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁

  • 尽量不要嵌套使用
    在这里插入图片描述

    private static String s1 = "筷子左";private static String s2 = "筷子右";public static void main(String[] args) {new Thread() {public void run() {while (true) {synchronized (s1) {System.out.println(getName() + "...拿到" + s1 + "等待" + s2);synchronized (s2) {System.out.println(getName() + "...拿到" + s2 + "开吃");}}}}}.start();new Thread() {public void run() {while (true) {synchronized (s2) {System.out.println(getName() + "...拿到" + s2 + "等待" + s1);synchronized (s1) {System.out.println(getName() + "...拿到" + s1 + "开吃");}}}}}.start();}

线程安全

  • Vector是线程安全的,ArrayList是线程不安全的
  • StringBuffer是线程安全的,StringBuilder是线程不安全的
  • Hashtable是线程安全的,HashMap是线程不安全的

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker拉取并配置Grafana

Linux下安装Docker请参考&#xff1a;Linux安装Docker 安装准备 新建挂载目录 /opt/grafana/data目录&#xff0c;准备用来挂载放置grafana的数据 /opt/grafana/plugins目录&#xff0c;准备用来放置grafana的插件 /opt/grafana/config目录&#xff0c;准备用来挂载放置graf…

取模运算符在数组下标的应用

什么是取模运算符%&#xff1f; 定义&#xff1a; a mod b&#xff0c;设a、b属于正整数且b>0&#xff0c;如果q、r属于正整数满足aq*br&#xff0c;且0≤r<b&#xff0c;则定义&#xff1a; a mod b r 注意&#xff1a;取模运算符两侧的除数和被除数都是整数&#xff…

Debootstrap 教程

文章目录 Debootstrap 教程安装 debootstrap使用 debootstrap运行 debootstrap进入新的系统结束语 Debootstrap 教程 debootstrap 是一个用于在 Debian-based 系统上创建一个基本的 Debian 系统的工具。它可以用于创建 chroot 环境、容器或者为新的系统安装做准备。 安装 deb…

分布式数据库架构:高可用、高性能的数据存储

在现代信息时代&#xff0c;数据是企业发展的核心。为了支持海量数据的存储、高并发访问以及保证数据的可靠性&#xff0c;分布式数据库架构应运而生。分布式数据库架构是一种将数据存储在多个物理节点上&#xff0c;并通过一系列复杂的协调和管理机制来提供高可用性和高性能的…

Qt下拉菜单

1&#xff0c;QComboBox 2&#xff0c;setMenu()---设置下拉菜单 AI对话未来丨智能写作对话: setMenu()是QWidget类的一个成员函数&#xff0c;在Qt中用于将一个菜单作为一个控件的下拉菜单设置。具体来说&#xff0c;它会把相应的菜单对象与该控件关联&#xff0c;并在控件上…

Element通过v-for循环渲染的form表单校验

需求&#xff1a;有个表单信息是v-for渲染的&#xff0c;例如下图&#xff0c;通过循环遍历实现新增和删除模块&#xff0c;按照平时的写法实现校验&#xff0c;是不能实现我们想要的效果&#xff0c;根据这个需求&#xff0c;我找到了一个解决方法 1.HTML <el-form ref&qu…

ESD门禁管理系统的主要功能和优势

ESD门禁管理系统是一种用于控制和管理人员进出特定区域的系统。它通常由门禁控制器、门禁读卡器、门禁管理软件等组成。 ESD门禁管理系统的主要功能包括&#xff1a; 1. 门禁控制&#xff1a;通过门禁控制器实现对门禁设备的控制&#xff0c;如开关门、锁定门等。 2. 门禁验…

Go与Rust的对比与分析

Rust 和 Go 是两种现代语言&#xff0c;近年来获得了巨大的关注&#xff0c;每种语言都有自己独特的优势和权衡。在这篇文章中&#xff0c;我们将深入探讨 Rust 和 Go 之间的差异&#xff0c;重点关注性能、语言功能和其他关键因素&#xff0c;以帮助您针对您的开发需求做出明智…

【面试】项目经理面试题

文章目录 一、项目管理面试中通常会问到的问题1.项目管理软件工具知识2.做项目计划的技能3.人员管理技能4.沟通技巧5.方法论知识 二、问面试官的问题三. 面试系列推荐 一、项目管理面试中通常会问到的问题 1.项目管理软件工具知识 问题 1: 工期和工作量之间的差异是什么? 答案…

视频汇聚平台EasyCVR安防视频监控在地下停车场中的具体应用方案

一、背景 随着我国城市化进程的加快&#xff0c;汽车已成为人们生活中不可缺少的交通工具&#xff0c;但在许多城市中&#xff0c;买车容易&#xff0c;停车难的问题愈发突出。特别是在人群密集的商场、写字楼和地下停车场&#xff0c;车流量大、车况复杂。传统的人工判断方式耗…

5、Spring_DI注解开发

DI 注解开发 1.目前面临问题 建立 mapper public interface EmployeeMapper {void save(); }建立 mapper 实现类 Repository public class EmployeeMapperImpl implements EmployeeMapper {public void save(){System.out.println("保存员工信息");} }建立 service …

【Unity】如何制作小地图

我们为什么要制作小地图呢&#xff1f; 原因很简单&#xff1a; 导航和定位&#xff1a;小地图可以显示玩家当前位置以及周围环境的概览。这使得玩家能够更好地导航和定位自己在游戏中的位置&#xff0c;找到目标或避开障碍物。场景了解&#xff1a;通过小地图&#xff0c;玩…

芯片验证板卡设计原理图:446-基于VU440T的多核处理器多输入芯片验证板卡

基于VU440T的多核处理器多输入芯片验证板卡 一、板卡概述 基于XCVU440-FLGA2892的多核处理器多输入芯片验证板卡为实现网络交换芯片的验证&#xff0c;包括四个FMC接口、DDR、GPIO等&#xff0c;北京太速科技芯片验证板卡用于完成甲方的芯片验证任务&#xff0c;多任务…

大数据 算法

什么是大数据 大数据是指数据量巨大、类型繁多、处理速度快的数据集合。这些数据集合通常包括结构化数据&#xff08;如数据库中的表格数据&#xff09;、半结构化数据&#xff08;如XML文件&#xff09;和非结构化数据&#xff08;如文本、音频和视频文件&#xff09;。大数据…

Blazor Session设置

文章目录 前言SessionProtectedSessionStorage 类信息加密使用场景 代码部分Nuget扩展安装源码使用&#xff0c; 相关资料 前言 微软官方封装了一些浏览器的操作&#xff0c;其中就有Session的操作的封装 ProtectedSessionStorage 微软文档 因为我们知道&#xff0c;依赖注入…

【Redis】Redis哨兵模式

【Redis】Redis哨兵模式 Redis主从模式当主服务器宕机后&#xff0c;需要手动把一台从服务器切换为主服务器&#xff0c;需要人工干预费事费力&#xff0c;为了解决这个问题出现了哨兵模式。 哨兵模式是是一个管理多个 Redis 实例的工具&#xff0c;它可以实现对 Redis 的监控…

PHP自己的框架session()使用(完善篇六)

1、PHP自己的框架session() 2、session类&#xff08;SessionBase.php&#xff09; <?php class SessionBase {/*** 设置session*/public static function set($name, $data, $expire600){$session_data array();$session_data[data] $data;$session_data[expire] time…

零阶矩、一阶矩、二阶矩、…

数学中矩的概念来自物理学。在物理学中&#xff0c;矩是表示距离和物理量乘积的物理量&#xff0c;表征物体的空间分布。矩在统计学和图像中都有很重要作用&#xff0c;我们常用的Adam优化器其全称为自适应矩估计优化器。本文将介绍各阶矩的理解和不同场景的应用。 Key Words&a…

wazuh初探系列一 : wazuh环境配置

目录 方法一&#xff1a;一体化部署 安装先决条件 第一步、安装所有必需的软件包 第二步、安装Elasticsearch 1、添加 Elastic Stack 存储库 安装 GPG 密钥&#xff1a; 添加存储库&#xff1a; 更新源&#xff1a; 2、Elasticsearch安装和配置 安装 Elasticsearch 包…

2023年8月第3周大模型荟萃

2023年8月第3周大模型荟萃 2023.8.22版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、LLM-Adapters&#xff1a;可将多种适配器集成到大语言模型 来自新加坡科技设计大学和新加坡管理大学的研究人员发布了一篇题为《LLM-Adapters: An …