来学习线程啦

线程的相关概念

程序

简单点说:程序就是我们写的代码;也可以理解为:为完成特定任务,用某种语言编写的一组指令的集合

进程

  • 进程是指运行中的程序。
    比如:我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间
    当我们使用迅雷,又启动一个进程,操作系统将为迅雷分配新的内存空间
  • 进程是程序得一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程

什么是线程

  • 1,线程由进程创建的,是进程的一个实体。
    比如:在迅雷中正在下载的一个文件 就是一个进程
  • 2,一个进程可以拥有多个线程

其他相关概念

  • 1,单线程:在同一时刻,只允许执行一个线程
  • 2,多线程:在同一时刻,可以执行多个线程。
    比如:一个QQ进程,可以同时打开多个聊天框
  • 3,并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发
    在这里插入图片描述
  • 4,并行:同一时刻,多个任务同时执行。多核cpu可以实现并行。
    在这里插入图片描述

线程基本使用

创建线程的两种方式

继承Thread类,重写run方法

在这里插入图片描述

public class Thread01 {public static void main(String[] args) {Cat cat = new Cat();cat.start();//启动线程-> main 线程启动一个子线程 Thread-0    最终会执行 cat 的 run 方法//为什么调用start方法// (1)//  public synchronized void start() {//   start0();//   }// (2)//start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现//真正实现多线程的效果, 是 start0(), 而不是 run//        private native void start0();// 当 main 线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行//这时 主线程和子线程是交替执行..System.out.println("主线程的线程名" + Thread.currentThread().getName());for(int i = 0; i < 60; i++) {System.out.println("主线程 i=" + i);//让主线程休眠try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}class Cat extends Thread {int times = 0;@Overridepublic void run() {//重写run方法,写上自己的业务逻辑//该线程每隔1秒,在控制台输出"喵喵,我是小猫咪"while (true) {System.out.println("喵喵,我是小猫咪" + "线程名=" + (++times) + Thread.currentThread().getName());//让该线程休眠1秒try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (times == 80) {break;//当times到80,退出while,这是线程也就退出}}}
}

调用start()方法示意图
在这里插入图片描述
注意:主线程和子线程会交替执行(并发/并行)

实现Runnable接口,重写run方法

说明:
1,java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时再用继承Thread类方法来创建线程显然不可能了
2,java设计者们提供了另一个方式创建线程,就是通过实现Runnable接口来创建线程

在这里插入图片描述

public class Thread02 {public static void main(String[] args) {Dog dog = new Dog();
//        dog.start();// public Thread(Runnable target) {//        this(null, target, "Thread-" + nextThreadNum(),//代理模式: Thread类有个如上构造器,需要传入Runnable类型的参数//                               Dog正好实现了Runnable类型//                               Dog类型可以代理Runnable类型传入Thread thread = new Thread(dog);thread.start();}
}
class Dog implements Runnable{int times = 0;@Overridepublic void run() {while (true) {System.out.println("hi" + (++times) + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (times==10) {break;}}}}

线程使用应用案例-多线程执行

在这里插入图片描述

public class Thread03 {public static void main(String[] args) {T1 t1 = new T1();T2 t2 = new T2();Thread thread = new Thread(t1);Thread thread1 = new Thread(t2);thread1.start();//启动第 1 个线程thread.start();//启动第 2 个线程}
}
class T1 implements Runnable{int count = 0 ;@Overridepublic void run() {while (true){System.out.println("hello,world"+(++count)+Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (count==10){break;}}}
}
class T2 implements Runnable {int count = 0;@Overridepublic void run() {while (true) {System.out.println("hi" + (++count) + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (count == 10) {break;}}}
}

线程如何理解

  • 通过主线程创建不同的线程去做不同的事;

在这里插入图片描述

  • 所有线程运行完之后进程才退出
    在这里插入图片描述

创建线程两种方式的区别(继承Thread 和 实现Runnable 的区别)

  • 1,通过java的设计来看,通过继承Thread 或者实现Runnable 接口来创建线程本质上没有区别。本质上Thread类本身即实现了Runnable接口
  • 2,实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的机制,建议使用Runnable
    在这里插入图片描述

出现超卖现象

public class Sell {public static void main(String[] args) {SellTicket sellTicket = new SellTicket();//这里会出现超卖现象new Thread(sellTicket).start();new Thread(sellTicket).start();new Thread(sellTicket).start();}
}class SellTicket implements Runnable {public int ticketNum = 100;@Overridepublic void run() {while (true) {if (ticketNum <= 0) {System.out.println("售票结束");break;}try {Thread.sleep(12);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--ticketNum));}}
}

线程终止

基本说明

  • 1,当线程完成任务后,会自动退出
  • 2,还可以通过使用变量来控制run方法退出的方式停止线程
public class ThreadExit {public static void main(String[] args) {AThread aThread = new AThread();Thread thread = new Thread(aThread);thread.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}//4,更改AThread类中的loop变量,停止线程aThread.setLoop(false);}
}
class AThread implements Runnable{//1,定义标记变量,默认为trueprivate boolean loop =true;//2,将loop作为循环条件@Overridepublic void run() {while (loop){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("AThread运行中");}}//3,提高公共的set方法,用于更细looppublic void setLoop(boolean loop) {this.loop = loop;}
}

线程常用方法

常用方法第一组

  • 1,setName:设置线程名称,使之与参数name相同
  • 2,getName:返回该线程的名称
  • 3,start:使该线程开始执行;java虚拟机底层调用该线程的start0 方法
  • 4,run:调用线程对象 run 方法
  • 5,setPriority:更改线程的优先级
  • 6,getPriority:获取线程的优先级
  • 7,sleep:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  • 8,interrupt:中断线程

注意事项

  • 1,start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会再启动新线程

  • 2,线程优先级的范围
    在这里插入图片描述

  • 3,interrupt:中断线程。但并没有真正的结束线程,所以一般用于中断正在休眠的线程

  • 4,sleep:线程的静态方法,使当前线程休眠

方法的案例演示

public class ThreadMethod {public static void main(String[] args) {ThreadDemo threadDemo = new ThreadDemo();threadDemo.setName("小笼包");//设置优先级threadDemo.setPriority(Thread.MIN_PRIORITY);threadDemo.start();for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("hi"+i);}//测试优先级System.out.println(threadDemo.getName()+"线程的优先级"+threadDemo.getPriority());threadDemo.interrupt();//当执行到这里,就会中断线程的休眠}
}
class ThreadDemo extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"吃包子~~~"+i);}try {System.out.println("休眠中~~~~~");Thread.sleep(20000);} catch (InterruptedException e) {//当该线程执行到Interrupt方法时,会catch一个异常,可以加入自己的业务代码//InterruptedException 是捕获到一个中断异常System.out.println(Thread.currentThread().getName()+"被interrupt了");}}
}

常用方法第二组

在这里插入图片描述

public class ThreadMethod02 {public static void main(String[] args) throws InterruptedException {T t = new T();t.start();for (int i = 0; i < 5; i++) {System.out.println("主线程(小弟)"+i);Thread.sleep(500);System.out.println("主线程(小弟)让 子线程(老大)先吃");
//            线程插队:先执行完插入的所有任务(t 子线程中),再执行主线程任务t.join();//礼让 不一定成功
//            Thread.yield();}}
}
class T extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("Hello~~~"+i);}}
}

用户线程和守护线程

  • 1,用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  • 2,守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  • 3,常见的守护线程:垃圾回收机制

守护线程的案例

public class ThreadMethod03 {public static void main(String[] args) {MyDaemon myDaemon = new MyDaemon();//为MyDaemon类 添加守护线程:当所有的用户线程结束,守护线程自动结束myDaemon.setDaemon(true);myDaemon.start();for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("主线程");}}
}
class MyDaemon extends Thread{@Overridepublic void run() {for(;;){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("守护线程");}}
}

线程的声明周期

JDK 中用 Thread.State 枚举表示了线程的几种状态

  • 五种状态
    在这里插入图片描述
  • 线程状态转换图(将RUNNABLE一种状态 分为 Ready 和 Running 两种状态 )
    在这里插入图片描述
    状态的示例代码
public class ThreadState_ {public static void main(String[] args) throws InterruptedException {TT tt = new TT();System.out.println(tt.getName()+"状态"+tt.getState());//状态:NEW  RUNNABLEtt.start();while (Thread.State.TERMINATED!=tt.getState()){System.out.println(tt.getName()+"状态"+tt.getState());//状态:TIMED_WAITINGThread.sleep(500);}System.out.println(tt.getName()+"状态"+tt.getState());//状态:TERMINATED}
}
class TT extends Thread{@Overridepublic void run() {while (true){for (int i = 0; i < 10; i++) {System.out.println("hi"+i);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;}}
}

线程的同步Synchronized

线程的同步机制

  • 1,在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整型
  • 2,也可以理解为:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作(上厕所)

同步具体方法

  • 1,同步代码块
    Synchronized(对象){//得到对象的锁,才能操作同步代码
    //需要被同步代码
    }
  • 2,Synchronized还可以放在方法声明中,表示整个方法-为同步方法
    public Synchronized void m(String name){
    //需要被同步的代码}
  • 3,如何理解:
    就好像某小伙伴上厕所前先把门关上(上锁),那么其他小伙伴就可以使用厕所了
public class Sell {public static void main(String[] args) {SellTicket sellTicket = new SellTicket();//这里会出现超卖现象new Thread(sellTicket).start();new Thread(sellTicket).start();new Thread(sellTicket).start();}
}class SellTicket implements Runnable {public int ticketNum = 100;public synchronized void m(){//同步方法,在同一时刻,只能有一个线程来执行sell方法while (true) {if (ticketNum <= 0) {System.out.println("售票结束");break;}try {Thread.sleep(12);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--ticketNum));}}@Overridepublic  void run() {m();}
}

互斥锁

基本介绍

  • 1,Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
  • 2,每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
  • 3,关键字Synchronized来与对象的互斥锁联系,当某个对象用Synchronized修饰时,表面该对象在任一时刻只能由一个线程访问
  • 4,同步的局限性:导致程序的执行效率要降低
  • 5,同步方法((非静态的)的锁可以是this,也可以是其他对象(要求是同一对象)
  • 6,同步方法((静态的)的锁为当前类本身

注意细节

  • 1,同步方法如果没有使用static修饰:默认锁对象为this
  • 2,如果方法使用static修饰,默认锁对象:当前类.class
  • 3,实现的落地步骤:
    先分析上锁的代码
    选择同步代码块或同步方法
    要求多个线程的锁对象为同一个即可
public class Sell {public static void main(String[] args) {
//        SellTicket sellTicket = new SellTicket();
//        //这里会出现超卖现象
//        new Thread(sellTicket).start();
//        new Thread(sellTicket).start();
//        new Thread(sellTicket).start();SellTicket sellTicket2 = new SellTicket();Thread thread1 = new Thread(sellTicket2);thread1.start();Thread thread2 = new Thread(sellTicket2);thread2.start();Thread thread3 = new Thread(sellTicket2);thread3.start();}
}class SellTicket implements Runnable {public int ticketNum = 100;private boolean loop = true;Object object = new Object();//1. public synchronized static void m1() {} 锁是加在 SellTicket03.class//2. 如果在静态方法中,实现一个同步代码块./*synchronized (SellTicket.class) {System.out.println("m2");}*///1. public synchronized void sell() {} 就是一个同步方法//2. 这时锁在 this 对象//3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象public /*synchronized*/ void m(){//同步方法,在同一时刻,只能有一个线程来执行sell方法synchronized (/*this*/object) {while (loop) {if (ticketNum <= 0) {System.out.println("售票结束");break;}try {Thread.sleep(12);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数" + (--ticketNum));}}}@Overridepublic  void run() {m();}
}

线程的死锁

基本介绍

多个线程都占用了对方的锁资源,但不肯想让,导致了死锁,在编程中一定要避免死锁发生

释放锁

下面操作会释放锁

  • 1,当前线程的同步方法、同步代码块执行结束
    案例:上厕所
  • 2,当前线程在同步代码块、同步方法中遇到break、return
    案例:没有正常的完事,经历叫他修改bug,不得已出来
  • 3,当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
    案例:没有正常的完事,发现忘带纸,不得已出来
  • 4,当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
    案例:没有正常的完事,觉得需要酝酿,所以出来等会再进去

下面操作不会释放锁

  • 1,线程执行同步块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法,暂停当前线程执行,不会释放锁
    案例:上厕所,太困了,在坑位上眯一会
  • 2,线程执行同步代码块时,其他线程调用了该线程的suspend()方法,将该线程挂起,该线程不会释放锁
    提示:应尽量避免使用suspend()和resume()来

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

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

相关文章

记录一期Typecho WebShell木马渗透的经历

我创建了一个Typecho的轻量博客,之前一直是本地运行,最近才上了公网,平时自己也是粗心大意,把密码也写在第一篇博文里面 有一天,我突发奇想的想要提交更新,本博客是通过git进行代码版本管理的避免自己修改官方源码出现了问题,无法还原,也定时备份SQL, 然后莫名其妙的发现多了…

论文阅读:Self-Consistency Improves Chain of Thought Reasoning in Language Models

思维链 prompt 与预训练的大型语言模型相结合&#xff0c;在复杂的推理任务上取得了令人鼓舞的结果。在本文中&#xff0c;作者提出了一种新的解码策略&#xff0c;即自我一致性&#xff08;self-consistency&#xff09;&#xff0c;以取代思维链 prompt 中使用的 naive 贪婪解…

详述进程的地址空间

进程的地址空间 合法的地址 (可读或可写) 代码 (main, %rip 会从此处取出待执行的指令)&#xff0c;只读数据 (static int x)&#xff0c;读写堆栈 (int y)&#xff0c;读写运行时分配的内存 (???)&#xff0c;读写动态链接库 (???) 非法的地址 NULL&#xff0c;导致 se…

串联所有单词的子串 ---- 滑动窗口

题目链接 题目: 分析: 我们上次做的题目, 是找到所有字符的异位词, 和这道题有些类似, 使用记录有效字符的个数找到子字符, 此题无非是把字符变成了字符串题目回顾 有一下几方面不同, 我们以示例1为例: 1. 哈希表 上次我们使用的是哈希数组, 因为数组的下标可以是字符, 现…

织梦dedecms企业网站模板安装教程

很多新手在拿到织梦模板后不知道如何安装&#xff0c;所以&#xff0c;云部落(Yunbuluo.Net)资源网专门整理了一份图文版织梦模板通用安装教程&#xff0c;希望对大家有所帮助。 第一步&#xff1a; 将域名解析绑定好之后&#xff0c;上传下载的模板至您的WEB根目录中&#xf…

YOLOv5独家改进:backbone改进 | 微软新作StarNet:超强轻量级Backbone | CVPR 2024

💡💡💡创新点:star operation(元素乘法)在无需加宽网络下,将输入映射到高维非线性特征空间的能力,这就是StarNet的核心创新,在紧凑的网络结构和较低的能耗下展示了令人印象深刻的性能和低延迟 💡💡💡如何跟YOLOv5结合:替代YOLOv5的backbone 收录 YOL…

C51 单片机编程模板及编码规范

文章目录 一、C51 单片机模板创建1. 新建工程及选型2. 创建主程序文件3. 创建主程序的头文件4. 编译配置5. 其他 二、C51 的编码规范 在查阅了很多关于 C51 单片机的程序后&#xff0c;个人感觉目前网上有关 C51 单片机程序的质量参差不齐&#xff0c;很多程序的代码风格及其糟…

YOLOv9改进策略 | 低照度图像篇 | 2024最新改进CPA-Enhancer链式思考网络(适用低照度、图像去雾、雨天、雪天)

一、本文介绍 本文给大家带来的2024.3月份最新改进机制&#xff0c;由CPA-Enhancer: Chain-of-Thought Prompted Adaptive Enhancer for Object Detection under Unknown Degradations论文提出的CPA-Enhancer链式思考网络&#xff0c;CPA-Enhancer通过引入链式思考提示机制&am…

详解xlswriter 操作Excel的高级应用conditional_format

在文章详解xlsxwriter 操作Excel的常用API-CSDN博客 我们介绍了xlswriter 基础api的使用情况&#xff0c;在实际工作中我们经常会遇到下面的需求&#xff0c;cell满足某某条件时&#xff0c;进行对应的格式化处理。这时候我们可以使用conditional_format的函数&#xff0c;他允…

C语言----斐波那契数列(附源代码)

各位看官们好&#xff0c;当我写了上一篇博客杨辉三角后&#xff0c;有一些看官叫我讲一下斐波那契数列。对于这个大家应该是有了解的。最简单的规律就是f(n)f(n-2)f(n-1)。就是当前是前两项之和&#xff0c;然后下标1和0都是1.从第三项开始计算的。那么我们知道规律&#xff0…

echarts map地图添加背景图

给map地图添加了一个阴影3d的效果&#xff0c;添加一张背景图&#xff0c;给人感觉有3d的效果 具体配置如下&#xff1a; html代码模块&#xff1a; <div class"echart_img" style"position: fixed; visibility: hidden;"></div><div id&q…

【正则表达式】1、元字符的认识与分类

1、元字符的概念 正则表达式的常见功能&#xff0c;分别是校验数据的有效性、查找符合要求的文本以及对文本进行切割和替换等操作。 我想你一定在办公软件&#xff0c;比如 Word、Excel 中用过这个功能。你可以使用查找功能快速定位关注的内容&#xff0c;然后使用替换&#xf…