世界信息:程序、进程和线程——多线程的创建方法
目录
(资料图片)
程序、进程和线程的概念
多线程的优点
Thread类关于多线程的创建
Thread类的相关方法
线程的调度
线程的五种状态
线程的同步
总结同步方法
衍生内容————单例设计模式
死锁问题
锁的概念
sleep()和wait()的异同
首先要明确几个概念
程序、进程和线程的概念
程序:完成特定任务,用某种特殊的语言编写的一组指令的集合
进程:是执行路径,一个进程同一时间并行或者正在运行的程序
线程:是执行路径,一个进程同一时间并行或者执行多个进程,就是多线程
注:进程中也有可能有多个线程
CPU也分为多核CPU和单核CPU
单核CPU:实际上进行的是某种意义上的假CPU,一个CPU同时做好多事,如果一个没有准备好,就先将该事件挂起,去进行别的,可以用一张图来表示
多核CPU(取决与主频来利用哪个):多核CPU就相当于多个单核CPU工作
同时也要解释两个词的含义
并行:多个CPU任务一起进行
并发:一个CPU做多个任务
注:并发只是看上去“同时”,但是实际上只是在CPU上进行高速的切换任务,以至于仅仅是看上去是同时,并行才是真正意义上的同时
多线程的优点
1、提高应用程序的响应
2、提高CPU的利用率
3、改善程序结构,每个线程独立运行,互不干扰,便于修改
提到多线程,就不得不提一个特殊的类
Thread类关于多线程的创建
方法一:
1、创建一个继承于Thread类的子类
2、重写Thread类中的run()
3、创建Thread类子类的对象(要在主线程上创建)、
4、通过对象去调用start()
想要创建一个多线程的代码如下
//主函数中的体现为//1、创建了继承Thread的子类//在继承Thread中的表现为public class ExtendsThread extends Thread { @Override //2、此处为标准的对于run()函数重写 //对run()函数的重写就相当于对于这一条线程中你想做的所有任务 public void run() { super.run(); for(int i=0;i<=20;i++) { System.out.println(i); } }}public class ThreadTest { public static void main(String[]args) { //3、创建了继承Thread子类的对象 Thread et=new ExtendsThread(); //4、通过对象调用了start() et.start(); //调用start()之后就开启多线程 }}
此处需要注意的是
1、run方法的重写:将这个线程要执行的所有操作全部都声明在run方法中
2、et.run()也能在主函数中直接调用,也能完整的执行在run方法中的指令,但是不能体现多线程,就仅仅是将指令完成,et.run()就仅仅只是调用方法看
3、不能够让已经start()的线程再去重启线程
4、可以创建多个对于ExtendsThread的对象,此时这个对象可以再次开始start(),相当于多开了一个线程,只不过执行的是相同内容
5、匿名子类与匿名对象同样适用
public class ThreadTest { public static void main(String[]args) { Thread et=new ExtendsThread(); //此处为体现多线程,同时开启两个线程 et.start(); //以下即为匿名子类 //直接开启多线程 new Thread(){ public void run() { super.run(); for(int i=0;i<=10;i++) { System.out.println(i+"#"+i); } } }.start(); }}public class ExtendsThread extends Thread { @Override public void run() { super.run(); for(int i=0;i<=10;i++) { System.out.println(i+"*"+i); } }}
第一次的执行结果
方法二:
1、创建一个实现了Runnable接口的类
2、实现Runnable接口中的抽象方法
3、创建实现类对象
4、将此对象作为参数传至Thread类的构造器,创造Thread类的对象
5、利用Thread()类的对象调用start()
public class RunnalbeThread implements Runnable//1、创建一个实现Runnable的类{ @Override//2、类中重写Runnable的方法,也就是run方法 public void run() { for(int i=1;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } }}public class ThreadTest { public static void main(String[] args) { Thread rt=new Thread(new RunnalbeThread()); //3、创建一个对应类的对象 //4、将这个对象传入到Thread的构造器 rt.start(); //5、用这个对应的Thread对象来继续调用start() rt.setName("线程3"); for(int i=1;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i+"-"+Thread.currentThread().isAlive()); } }}
在这个地方,如果没有创建匿名对象(对于实现Runnable的实现类),一个实现类的对象,可以多次传入到Thread的构造器里面,创造更多的线程
两种方法的比较
继承法(方法一)由于Java的单继承性,导致如果需要继承Thread类的类由原本的一套体系,可能会影响该代码的实现,由此看来,实现接口的方式是更加活泛的,更自由。
实操中优先选择Runnable接口的方式
1、实现的方式没有单继承性的限制
2、实现的方式更适合多个线程共享数据的情况
注:Thread类也实现了Runnable接口
Thread类的相关方法
1、String getName();
返回线程名称
2、void setName(String name);
设置线程名称
public static void main(String[] args) { Thread et = new ExtendsThread(); et.setName("线程--1"); System.out.printf(et.getName());}
运行结果
此处需要注意的是,主线程也是可以命名的,如以下代码
public class ThreadTest { public static void main(String[] args) { Thread et = new ExtendsThread(); Thread.currentThread().setName("主线程"); System.out.printf(Thread.currentThread().getName()); }}
运行结果如下
3、currentThread()方法
静态方法,返回当前执行此代码的线程(对象)
4、yield()方法
释放当前CPU的执行权
也存在当我们释放完执行权之后,CPU再次将执行权分配给目前线程的情况
5、join()方法
相当于在原本的线程1上,让另一个线程2截断,知道这个线程2执行结束,否则不再进行线程1(在线程1之中调用线程2的join方法)
代码测试如下
public class ExtendsThread extends Thread{ @Override public void run() { super.run(); for(int i=0;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i+"*"+i); } }}public class ExtendsThread2 extends Thread{ public void run() { super.run(); for(int i=0;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i+"#"+i); } }}public class ThreadTest { public static void main(String[] args) { Thread et = new ExtendsThread(); Thread et2=new ExtendsThread2(); et.start(); et2.start(); et.setName("线程1"); et2.setName("线程2"); Thread.currentThread().setName("主线程"); for(int i=0;i<=20;i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i%5==0) { try { et2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } }}
测试结果如下
当主线程的i跑到5的时候,此时调用了et.join()和et2.join()此时的主线程已经被挂起了,直到线程1和线程2运行完之后,才会继续主线程的进行。
6、stop()
强制结束线程,可以提前结束线程的生命周期。(不推荐使用stop()结束进程)
public class ExtendsThread extends Thread{ @Override public void run() { super.run(); for(int i=0;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i+"*"+i); if(i==5) { Thread.currentThread().stop(); //此处用stop强制停止了 //当i=5的时候强制停止线程 } } }}public class ThreadTest { public static void main(String[] args) { Thread et = new ExtendsThread(); Thread et2=new ExtendsThread2(); et.start(); et.setName("线程1"); Thread.currentThread().setName("主线程"); for(int i=1;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } }}
测试结果
如图所示,线程1确实只进行到i=5的时候
7、sleep(long millitime)
强制线程进入休眠,单位是毫秒
在指定时间内强制休眠
需要注意的是,对某个线程使用sleep的话,该线程就会进入到挂起状态,在指定时间挂起。相当于主动让出了CPU的执行权。
8、isAlive()
判断当前线程是否存活
举例如下
public class ExtendsThread extends Thread{ @Override public void run() { super.run(); for(int i=0;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i+"-"+Thread.currentThread().isAlive()); } }}public class ThreadTest { public static void main(String[] args) { Thread et = new ExtendsThread(); Thread et2=new ExtendsThread2(); et.start(); et.setName("线程1"); Thread.currentThread().setName("主线程"); for(int i=1;i<=10;i++) { System.out.println(Thread.currentThread().getName()+":"+i+"-"+Thread.currentThread().isAlive()); } System.out.println(et.isAlive()); }}
结果如下
如图所示,在代码的最后,et所开启的线程已经结束,所以此时打印出来的false
线程的调度
线程的进行主要是看时间片,一般情况下,多个线程都是并发,所以对于CPU的执行权一般是进行抢夺,高优先级的线程优先抢夺CPU的执行权。
说到这里就不得不提到线程的优先等级(这里的优先级都是在线程诞生的时候就是设置好的,默认为5)
>MAX_PRIORITY:10
>MIN_PRIORITY:1
>NORM_PRIORITY:5
也有两个方法是关于线程的优先级
1、getPriority():返回线程优先级
2、setPriority(int newPriority):改变线程的优先级
高优先级抢占低优先级的线程的CPU执行权,但是是从概率上而言,高优先级的线程有更大的概率去执行CPU
线程的五种状态
1、新建:当一个Thread类或其子类的声明并创建时,新生线程处于此状态
2、就绪:当线程被start()之后,就会进入队列等待CPU的时间片
3、运行:获得CPU资源,进入运行状态,run定义了线程操作和功能
4、阻塞:在某种情况下,被人为挂起或执行输入输出,让出CPU的执行权
5、死亡:线程完成了全部工作或被提前强制性中止(stop),或者出现异常导致结束,比如join()会使线程被挂起,造成线程阻塞
线程的同步
线程的安全问题(不一定出现线程安全问题)
没有sleep()出现时,错误的概率小,但是安全问题总是要解决的
有可能会出现极端情况
此时带入一个场景,比如说一个线程代表一个窗口,一个售票窗口,线程每进行一次就挂起一次,会打印票号,但是如果正常进行,票号应该是连号,但是会出现如下情况
代码如下
public class RunnalbeThread implements Runnable{ public static int num=30; public static int tnum=1; @Override public void run() { while(num!=0) { if(num>0) { num--; tnum++; System.out.println(Thread.currentThread().getName()+":"+tnum); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }}public class ThreadTest { public static void main(String[] args) { Thread rt3 = new Thread(new RunnalbeThread()); Thread rt2 = new Thread(new RunnalbeThread()); Thread rt = new Thread(new RunnalbeThread()); rt2.start(); rt3.start(); rt.start(); rt3.setName("线程3"); rt.setName("线程1"); rt2.setName("线程2"); }}
代码测试结果如下
很明显的,会出现重号的现象
原因:当某个线程操作票的过程中,尚未完成操作,另一个线程参与进来,也对车票进行操作(相当于是共享数据)
如何解决
加锁
当一个线程在操作共享数据的时候,其他线程不能参与,直到线程a操作结束,其他线程才能开始操作。即使a处于阻塞状态,也不能被改变
方法一:同步代码块
synchronized(同步监视器){
需要被同步的代码}
说明:操作共享数据的代码,即为需要被同步的代码
同步监视器,俗称锁,可以随意扔一个对象进去
要求:多个线程要共用同一把锁,不能设置多个锁,此时不能使用匿名
缺点:操作同步代码时,仅能有一个线程操作,其他的都在等待,相当于是一个单线程操作过程,相对而言效率会很低
此时会出现一个锁不唯一的问题,由于锁的创建在Thread的子类中,但是使用此方法创造进程需要newThread的子类的对象,此时会new出很多锁,此时最好的解决方案就是把锁进行static
方法展示
public class RunnalbeThread implements Runnable{ public static int num=30; @Override public void run() { while(num!=0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (RunnalbeThread.class) { if(num>0) { num--; System.out.println(Thread.currentThread().getName()+":"+num); } } } }}public class ThreadTest { public static void main(String[] args) { Thread rt3 = new Thread(new RunnalbeThread()); Thread rt2 = new Thread(new RunnalbeThread()); Thread rt = new Thread(new RunnalbeThread()); rt2.start(); rt3.start(); rt.start(); rt3.setName("线程3"); rt.setName("线程1"); rt2.setName("线程2"); }}
代码中,把对于所有共享数据的操作全部都包起来了,达到监视的作用
结果如下
还有一个需要注意的点就是如果是用接口实现的方法创建的线程,可以考虑使用this的,之所以继承法不能使用,是因为其依靠创造他本身的对象来创造线程,但是实现类只创造一个对象,其他对象都是利用Thread进行创造的。
但是我的代码中,监视器之后的锁就不能使用this,因为在主函数中,我用的创建方法并不是一个对象传入到Thread的构造器中,我使用了匿名对象,如果使用this,每一次的锁都是不一样的锁,无法起到监视作用了
同时,在我的代码中,使用了synchronized (类名.class)这种方式,在这里需要注意的是,类本身也是一个对象,类仅加载一次,与每次new完之后出现的新对象不同。所以在我看来,类是一个完美的锁,不会出现重复的现象。
也需要注意对于同步代码的包装。要注意包装的范围,少包不能解决安全问题,包多了会影响效率,而且也容易出现新的问题。
方式二:
1、同步方法实现Runnable接口
synchronized可以修饰方法,但是需要符合题意,一般情况下不建议使用
在同步方法的内部,就和使用synchronized包起来是一个效果
使用同步方法时,同步监视器就是this
2、同步方法继承Thread类的方法
对于继承法而言,很明显不能直接加synchronized,加了synchronized之后,会自动使用this作为监视器,很显然不行,此时应该将方法改成静态
总结同步方法
1、仍涉及同步监视器,只是不需要显式声明
2、非静态的同步方法是this,静态方法的监视器视为当前类本身
衍生内容————单例设计模式
1、懒汉式(线程安全)
先来分析一下,在原本对于懒汉式的代码中,线程安全可能会出现的部位
public class Bank { private Bank(){} private static Bank instance=null; public static Bank getInstance() { if(instance==null) { instance=new Bank(); } //在此段就容易出现堵塞或者就绪,当多线程在此处参与时,设线程a、线程b //a判断了instance==null,已经进入了语句,此时CPU将执行权切换给了b或 //a由于某种原因阻塞了,那么此时可能就不仅仅创建了一个对象 return instance; }}//而在关于单例式操作,同时满足有多个线程,有共享数据这两个条件,可以实现线程安全
本质上就是线程a、b抢锁,谁先抢到就谁先造
如果想用同步方法,在本例中就可以直接将getInstance这个方法直接使用synchronized直接修饰,就可以解决线程安全问题
如果想使用同步代码块,就可以使用synchronized将getInstance这个方法中的内容直接包裹,并且利用Bank.class对代码进行监视(效率差)
同步代码块——方法一
public class Bank { private Bank(){} private static Bank instance=null; public static Bank getInstance() { if(instance==null) { synchronized(Bank.class) { instance=new Bank(); } } return instance; }}
同步代码块——方法二
public class Bank { private Bank(){} private static Bank instance=null; public static Bank getInstance() { synchronized(Bank.class) { if(instance==null) { instance=new Bank(); } } return instance; }}
两个方法在使用上的区别不大,都可以正常使用,但是实际上方法一的效率更高
假设现在有线程1和线程2,当线程1率先抢到CPU控制权,先制造了对象,线程2在方法二中仍停留在synchronized语句上等待,一直到线程1制造完对象,线程2才能够进入if,判断失败之后离开该方法,但是在方法一中,线程2先进入判断,如果1已经造完对象了,那么线程2就会直接离开。线程2就不会再进入等待区。
死锁问题
不同的线程分别占用了对象所需资源不放,都在等对方放弃,形成死锁
>不出现异常,不出现提示,所有的线程阻塞,不再进行
使用同步的时候,一定要避免死锁问题出现
锁的概念
Lock实际上就是一个接口,需要有实现类
Lock接口的具体使用,主要是对其实现类:Reentrantlock的使用
Reentrantlock
这个类有两个构造器,有一个形参fair
如果fair是true,就遵循先入先出,按照abc顺序开锁
如果fair是false或者没有参数,那么就是abc抢锁,谁先抢到谁先开
1、实例化Reentrantlock
2、将同步代码放到try中,在try首行调用Reentrantlock的对象调用Lock(),也可以调用解锁,try-finally,其中不使用catch,只是想让finally无论如果先给Lock解锁,即使try过程有异常,也会给Lock解锁
(其实本质上也就是上锁,只不过Lock需要手动开锁,但是synchronized不需要,synchronized自动就会开锁)
synchronized和Lock的异同
synchronized机制在执行完同步代码块后自动释放同步监视器
Lock需要手动开锁,不然会一直锁定一个线程不放
基本都会使用synchronized,但是实际上更建议使用Lock
sleep()和wait()的异同
相同:都可以使当前线程进入阻塞
不同:
1、两个方法声明位置不同,Thread类中声明sleep(),Object类中声明wait()
2、调用范围不同,sleep()在任何场景都能调用,wait()必须使用在同步方法或者同步代码块中
3、关于是否释放同步监视器,如果二者都在同步中,sleep()不释放锁,但是wait()会释放锁
标签:
相关推荐:
最新新闻:
- 热消息:全球最权威的学术期刊之一——《科学》
- java的序列化机制是什么?java序列化ID的作用:世界热闻
- rolling(k)函数的用法详解 例子说明rolling函数的用法
- 天天时讯:cdn加速测试:又拍云每个月免费提供15g流量
- 什么是数据库索引?MySQL官方对索引的定义及索引优劣势分析:每日速读
- 微信支付如何关闭“自动扣费”?关闭微信自动扣费的方法步骤 全球报资讯
- 世界信息:程序、进程和线程——多线程的创建方法
- 与熊论道为什么解码不了?“熊”孩子都有哪些表现?_世界快看点
- 英国女子号称“现实版睡美人” 每天昏睡22小时
- 环球速递!《神领编年史》新更新上线 SE移除D加密技术
- 2023年2月PS5在英国的销量暴涨316%
- 全球消息!《死亡细胞》重返恶魔城DLC发布上市预告
- 北京最新天气预报:今天最高气温19℃,大气扩散条件和能见度较差
- Arkane确认所有版本《红霞岛》支持跨平台联机
- 太便宜了!精粤B760M主板仅需399元
- 天天微头条丨只能用功能机 司法部要求FTX创始人保释期间不能用智能手机
- 续航50短通勤 五星钻豹电动车1299元:全球热头条
- 索尼克之父中裕司认罪!承认内幕交易
- 环球视讯!微软Win11开始重新设计混音器
- 人民币是什么材料做的|即时看
- 《最终幻想16》不是基于夜光或虚幻引擎运行
- 全球讯息:《破坏领主》3月15日登陆主机 Endgame更新同时发布
- 《宝可梦:朱/紫》1.2更新后部分玩家存档被删除:环球要闻
- Wii U用户重创:任天堂下架马里奥赛车和斯普拉遁
- 烧白做法_烧白如何做
- 国外团队开发出突破性隐形眼镜 可防干眼症 速递
- 热消息:《忍者神龟:变种大乱斗》首曝中字预告 “龟”来依旧是少年
- 红楼梦十二金钗判词|环球热闻
- 当前热讯:男子用微缩模型还原高启强老家:做了11天 约400个配件
- 戴尔推出AW620M无线鼠标 DPI最高26000
- 焦点热议:百威亚太(01876):高端啤酒龙头国内市场“遇冷记”
- 外媒锐评《自杀小队》服务模式:都想学《命运》?
- 《大航海时代:起源》现已在Steam免费发售 支持中文配音
- 《古墓丽影》重启作发售10周年纪念 官方发文庆贺 新视野
- 修培刻灵“蕉仙素”是非药物疗法与现代生物科技的结晶
- 澳大利亚将对猫咪实行“宵禁” 逃跑出门或被处决
- 华语悬疑剧《模仿犯》新预告:3.31网飞独家上线:环球聚看点
- 一本院校回应招聘会有洗碗工岗位 不只面向学生
- 小米MIUI 14迎来最新升级!MIUI相机史诗级更新(附刷机包下载)
- 电视剧山河令演员表|环球观点
- 【当前热闻】荣耀发布四款MagicBook笔记本,主要特点一览
- 看片太爽了!极空间 Z4s NAS 售价新低:2899元
- 《龙珠斗士Z》官方宣布仍将调整平衡性 新更新稍后实施:环球速递
- 每日讯息!Furyu ARPG新作《恸哭机巧》OP动画公开
- 每日热文:CDPR发布《巫师3:狂猎》萝卜介绍 网友:不是叫葡萄吗
- 《美生中国人》制作花絮:杨紫琼、吴彦祖玩转校园_当前最新
- 新资讯:《火焰纹章:结合》DLC第三弹纹章士介绍影片公开
- 折叠屏手机首次出货量下滑,同比下降 26%
- 全球今头条!荣耀 MagicBook X 14/16 Pro 笔记本发布:4299 元起,13 代酷睿
- 抄底价!小米Redmi K50 Ultra 12+256G仅2619元|环球头条
- 抄底价!小米Redmi K50 Ultra 12+256G仅2619元|环球头条
- 折叠屏手机首次出货量下滑,同比下降 26%
- 饭制虚幻5《GTA:圣安地列斯》重制版预告:CJ前面跑 飞机后头追
- 雷军:小米造车进展超预期 预计明年上半年量产_世界球精选
- 环球热点!《如龙 维新·极》制作人采访:焕然一新的游戏体验!
- 传闻:《GTA6》2024年底推出 本体或削减内容用DLC补齐-看热讯
- 卧龙苍天陨落天柱派钥匙在哪?卧龙苍天陨落道士家的后院钥匙位置_今日热闻
- 5秒学会用抖音 但当代年轻人已不会用打印机甚至电脑:时代真变了
- “雷锋精神”在警营 伊春公安将爱“钉”入百姓家:天天快资讯
- 世界滚动:三微边设计!小米27英寸2K IPS显示器低至749元
- 环球精选!超大超爽!12G+512G旗舰机2149元
- 焦点关注:春季行情!苏宁易购举办专场供应商沟通会
- 直降500元!铭瑄MS-RTX4080 iCraft OC16G瑷珈独立显卡
- 每日快报!12代酷睿四核神U加持!天钡N-box迷你主机低至898元
- 游戏改编《龙与地下城》新预告:宝箱怪、噬脑怪亮相:今日最新
- 座位螺栓没拧紧!特斯拉在美召回3470辆汽车|焦点热闻
- 《怪物猎人崛起:曙光》推出冥渊龙和混沌黑蚀龙Q版毛绒玩具 售价196元
- 《卧龙:苍天陨落》PS5性能分析 身临其境般的体验_当前通讯
- 航空航天
- 33万买奥迪A7L 内部福利普通人别想:世界观热点
- 2022年全球光伏组件出货量top10!黑马“大明光福”首次入选_全球头条
- 京东百亿补贴3月6日全面上线 海信高清电视仅售755元
- 【环球速看料】京东百亿补贴全面上线:4899元买iPhone 14
- 焦点关注:国产游戏显卡直降1100元:已支持42款游戏
- 举义旗迎国货,一科技公司发起反ChatGPT号召
- 全球消息!《最后生还者》第八集IGN 9分:绝望使人坠入深渊
- 致敬经典 粉丝打造《最终幻想6》2D-HD重制版
- 天天视点!《最终幻想16》新宣传片 更多游戏细节介绍
- 天天新资讯:女子10元机选票擒双色球830万 连续多天没睡好觉
- 环球快看:刘亦菲捧花手机壳搜索量飙升2852%:换上能带来好运
- 《红霞岛》本周举办媒体预览活动 可以实机游玩 最新快讯
- 上海迪士尼重启握手拥抱合影!网友:已经等不及了_世界今亮点
- B站《三体》复播被观众狂刷好评:网友们是懂阴阳的-最新快讯
- R星设计总监:《GTAOL》专为单人游戏而设计
- 如何辨别蜂蜜真假?教你5个小窍门轻松辨别真假蜂蜜