2022.0727学习Java笔记之多线程编程(二)
多线程的操作方法
多线程的操作方法基本上都在Thread类之中定义的,所以研究方法也就是一个Thread类的翻译,那么下面主要看三组方法。
1.线程的命名和取得
在整个多线程的操作之中,名称是最重要的,因为多线程的运行状态是不确定的,那么系统只能根据名字来判断出是那一个线程,所以在为线程命名的时候不要有重复,更不要修改。
在Thread类里面提供了如下的几个方法进行名称操作:。
- 构造方法:public Thread(Runnable target, String name);·
- 设置名字:public final void setName(String name);·
- 取得名字:public final String getName()。
问题是线程的运行状态不确定,所以要想取得线程的名字,只能够取得当前正在执行的线程。那么在这种情况下,如果要想取得当前的线程对象,就必须依靠Thread类的方法:public static Thread currentThread()。
范例:取得线程名字
package cn.mldn.demo;class MyThread implements Runnable{public void run(){ //线程的主方法System.out.println(Thread.currentThread().getName());}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt).start() ;new Thread(mt).start() ;new Thread(mt,"带名字").start() ;}
}
发现此时如果设置了线程名称,那么就会返回设置的数据。如果没有设置线程名字会自动的进行命名,以保证每一个线程一定有个名字。
范例:观察如下代码
package cn.mldn.demo;class MyThread implements Runnable{public void run(){ //线程的主方法System.out.println(Thread.currentThread().getName());}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"自定义线程").start() ;mt.run(); //主方法之中进行调用}
}
现在发现通过“mt.run()”执行的时候取得的线程名称是main,所以可知:主方法也是一个线程,而新的问题就出现了,一直在讨论线程,但线程依附于进程,进程也在那里?
每当用户使用Java命令解释一个类的时候,对操作系统而言,都会默认启动一个Java的进程,而主方法只是这进程之中的一个线程而已。每一个JVM运行时至少要启动两个线程:主线程、GC垃圾回收线程。
2.线程休眠,线程的休眠指的是让程序暂缓执行,休眠方法:public static void sleep(long millis)throws InterruptedException;
范例:观察休眠
package cn.mldn.demo;class MyThread implements Runnable{public void run(){ //线程的主方法for(int x = 0; x<100; x++){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName());}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"线程A").start() ;new Thread(mt,"线程B").start() ;new Thread(mt,"线程C").start() ;new Thread(mt,"线程D").start() ;new Thread(mt,"线程E").start() ;}
}
由于线程资源的间隔很短,那么可以简单地理解为,多个线程有可能会同时进入到方法执行。
3.线程的优先级
从理论上讲,线程的优先级越高,越有可能最先执行。但这也只是可能,如果想进行线程的优先级操作,可以使用如下的两个方法:
- 设置优先级:public final void setPriority(int newPriority);
- 取得优先级:public final int getPriority();
对于优先级在Thread类之中定义了三个常量:
- 最高优先级:public static final int MAX_PRIORITY, 10;
- 中等优先级:public static final int NORM_PRIORITY, 5;
- 最低优先级:public static final int MIN PRIORITY, 1。
范例:观察优先级改变
package cn.mldn.demo;class MyThread implements Runnable{public void run(){ //线程的主方法for(int x = 0; x<20; x++){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName());}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();Thread mt1 = new Thread(mt,"线程A");Thread mt2 = new Thread(mt,"线程B");Thread mt3 = new Thread(mt,"线程C");mt1.setPriority(Thread.MIN_PRIORITY);mt3.setPriority(Thread.MAX_PRIORITY);mt1.start();mt2.start();mt3.start();}
}
问题:那么主线程的优先级是多少呢
public class TextDemo {public static void main(String[] args){System.out.println(Thread.currentThread().getPriority());}
}
主线程只是一个中等优先级。
线程的同步与死锁
1.线程的同步
当多个线程访问同一资源的时候一定需要考虑到同步问题,那么下面首先经过一个简单的买票程序,来观察同步问题的情况。
范例:观察问题
package cn.mldn.demo;class MyThread implements Runnable{private int ticket = 8 ;public void run(){ //线程的主法for(int x = 0 ; x < 20 ; x ++ ){if(this.ticket > 0){System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);}}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"票贩子A").start();new Thread(mt,"票贩子B").start();new Thread(mt,"票贩子C").start();new Thread(mt,"票贩子D").start();new Thread(mt,"票贩子E").start();}
}
那么在这个时候就发生了不同步的情况,而发生的原因也很简单。对于整个卖票过程实际上是两步完成的
- 第一步:使用if语句判断是否有票;
- 第二步:进行票数的修改。
但是此时有可能会出现这样一种情况:如果说现在只有最后一张票了,可以满足if条件判断,所以线程可以通过if语句拦截,可是在修改票之前出现了一个延迟操作,那么有可能在这个延迟的过程之中,又出现了其它线程进入到方法中,由于此时没有修改票数,那么这个线程也可以满足if条件判断,后面的线程依次类推,所以当休眠时间已过,进行票数修改的时候,都将在已有的票数上修改,自然就有可能出现负数。
那么既然已经清楚了问题的产生原因,下面就必须进行问题的解决,问题的解决关键是需要一把锁。如果要想上好这把“锁”,则可以采用两种方式完成:同步代码块、同步方法。 I
范例:使用同步代码块
同步代码块是使用synchronized关键字定义的代码块,但是在进行同步的时候一定要设置好一个同步对象,所以这个同步对象一般使用当前对象this表示。
package cn.mldn.demo;class MyThread implements Runnable{private int ticket = 8 ;public void run(){ //线程的主法for(int x = 0 ; x < 20 ; x ++ ){if(this.ticket > 0){try{Thread.sleep(10000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);}}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"票贩子A").start();new Thread(mt,"票贩子B").start();new Thread(mt,"票贩子C").start();new Thread(mt,"票贩子D").start();new Thread(mt,"票贩子E").start();}
}
此时的程序执行速度变得明显缓慢了,因为需要一个个排队进行,就好比你们上网和去银行取钱。一定是上网快。虽然性能变慢了,但是数据的安全性提高了,也就是说异步处理(不加synchronized)和同步处理(synchronized)的使用区别也在于此。
范例:同步方法实现
如果在一个方法的声明上使用synchronized关键字,则表示此方法是一个同步方法。
package cn.mldn.demo;class MyThread implements Runnable{private int ticket = 8 ;public void run(){for(int x = 0 ; x < 20 ; x ++ ){this.sale();}}public synchronized void sale(){ //线程的主法if(this.ticket > 0){try{Thread.sleep(10000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"票贩子A").start();new Thread(mt,"票贩子B").start();new Thread(mt,"票贩子C").start();new Thread(mt,"票贩子D").start();new Thread(mt,"票贩子E").start();}
}
同步的核心意义:一个线程要等另一个线程执行完毕
2.死锁
死锁实际上是在项目运行过程之中产生的一种问题,简单说就是多个线程访问同一个资源的时候一定要考虑同步,但是同步会影响程序的性能,同时会提升数据的安全性,过的同步会出现死锁
线程通讯的经典案例--生产者和消费者
本程序的核心结构如下:首先定义两个类,一个是生产者线程类,另一个是消费者线程类生产者每年生产完一个数据之后,消费者要取走这些数据,那么假设现在的数据有两种:
title=小动物,content= 草泥马;
title=小金子,content=不是好孩子。
范例
package cn.mldn.demo;class Message{private String title;private String content;public void setTitle(String title){this.title = title;}public void setContent(String content){this.content= content;}public String getContent(){return content;}public String getTitle(){return title;}
}
class Productor implements Runnable{private Message msg;public Productor(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){if(x % 2 ==0){this.msg.setTitle("小动物");try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.msg.setContent("草泥马");}else{this.msg.setTitle("小金子");try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.msg.setContent("不是好孩子");}}}
}
class Custome implements Runnable{private Message msg;public Custome(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.msg.getTitle() + "," + this.msg.getContent());}}}
public class TextDemo {public static void main(String[] args){Message msg = new Message ();new Thread(new Productor(msg)).start();new Thread(new Custome(msg)).start();}
}
遗憾的是,这个时候出现了两个问题,
- 数据错乱
- 数据重复取出与重复设置
1.解决数据错位问题
想要解决数据错位问题,使用同步处理即可,代码如下
package cn.mldn.demo;class Message{private String title;private String content;public synchronized void set(String title , String content){this.title=title;try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.content=content;}public synchronized void get(){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.title + "," + this.content);}
}
class Productor implements Runnable{private Message msg;public Productor(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){if(x % 2 ==0){this.msg.set("小动物","草泥马");}else{this.msg.set("小金子","不是好孩子");}}}
}
class Custome implements Runnable{private Message msg;public Custome(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){this.msg.get();}}}
public class TextDemo {public static void main(String[] args){Message msg = new Message ();new Thread(new Productor(msg)).start();new Thread(new Custome(msg)).start();}
}
现在数据没有错乱,但是重复操作更严重了
2.解决数据重复操作
如果要想解决重复的操作必须加入等待与唤醒机制。而这样的处理机制是由Object类提供的方法支持,在Object类之中有如下的方法可以使用:
- 等待:public final void wait throws InterruptedException;
- 唤醒第一个:public final void notify();
- 唤全部:public final void notifyAll(),谁的优先级高就先执行。
范例:解决问题
package cn.mldn.demo;class Message{private String title;private String content;private boolean flag = true;//flag = true:表示可以生产,但是不能够取走//flag = false:表示可以取走,但不能生产public synchronized void set(String title , String content){if(this.flag == false){try{super.wait();}catch(InterruptedException e){e.printStackTrace();}}this.title=title;try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.content=content;this.flag = flag;super.notify();}public synchronized void get(){if(this.flag == true){try{super.wait();}catch(InterruptedException e){e.printStackTrace();}}try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.title + "," + this.content);this.flag = true;super.notify();}
}
class Productor implements Runnable{private Message msg;public Productor(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){if(x % 2 ==0){this.msg.set("小动物","草泥马");}else{this.msg.set("小金子","不是好孩子");}}}
}
class Custome implements Runnable{private Message msg;public Custome(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){this.msg.get();}}}
public class TextDemo {public static void main(String[] args){Message msg = new Message ();new Thread(new Productor(msg)).start();new Thread(new Custome(msg)).start();}
}
解释sleep()和wait()的区别
- sleep是Thread类定义的方法,在休眠之后可以自动唤醒;。
- wait()是Object类定义的方法,等待之后必须使用notify()或notifyAll()手工唤醒。
2022.0727学习Java笔记之多线程编程(二)相关推荐
- java8 函数式编程_您必须学习Java 8的函数式编程吗?
java8 函数式编程 我最近一直在研究Java 8,并掌握了Manning出版的" Java 8 In Action" . 让我印象深刻的第一件事是Java 8独特的销售主张是函 ...
- 您必须学习Java 8的函数式编程吗?
我最近一直在研究Java 8,并掌握了Manning出版的" Java 8 In Action" . 让我印象深刻的第一件事是Java 8的独特销售主张是函数式编程. 函数现在是一 ...
- Java中的多线程编程(超详细总结)
文章目录 Java中的多线程编程(超详细总结) 一.线程与多线程的概念 二.线程与进程之间的关系 三.一个线程的生命周期 四.多线程的目的和意义 五.线程的实现的方式 Java中的多线程编程(超详细总 ...
- 对比Java和.NET多线程编程
这篇文章以对比的方式总结Java和.NET多线程编程. 基本概念 多线程:很多开发语言都提供多线程编程支持,比如Java,C#. 并发(concurrent):即使对于单核CPU,我们也会采用多线程等 ...
- 【java笔记】网络编程概述
[java笔记]TCP通信程序_m0_52043808的博客-CSDN博客 [java笔记]网络编程:文件上传案例_m0_52043808的博客-CSDN博客 网络编程: 在一定协议下,实现两台计算机 ...
- 草根方式学习java中的多线程
草根方式学习java中的多线程 下面有具体的代码和截图 源码点这里 多线程即在同一时间,可以做多件事情(说白了,就是齐头并进) 单线程就是按部就班 创建多线程有2种方式,分别是继承线程Thread类, ...
- 【 反向传播算法 Back-Propagation 数学推导以及源码详解 深度学习 Pytorch笔记 B站刘二大人(3/10)】
反向传播算法 Back-Propagation 数学推导以及源码详解 深度学习 Pytorch笔记 B站刘二大人(3/10) 数学推导 BP算法 BP神经网络可以说机器学习的最基础网络.对于普通的简单 ...
- 【 卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10)】
卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10) 本章主要进行卷积神经网络的相关数学原理和pytorch的对应模块进行推导分析 代码也是通过demo实 ...
- 【 线性回归 Linear-Regression torch模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人(4/10)】
torch模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人 深度学习 Pytorch笔记 B站刘二大人(4/10) 介绍 至此开始,深度学习模型构建的预备知识已经完全准备完毕. 从本章开 ...
最新文章
- 转载 - sql分页优化
- 谷歌和 Facebook 是如何给工程师定职级和薪水的?
- 自动化测试===unittest配套的HTMLTestRunner.py生成html报告源码
- SAP Fiori Elements - how to set breakpoint to get converted xml view parsed by f
- 十四、PHP框架Laravel学习笔记——构造器的排序分组、子查询
- C++ STL 中提供的算法
- mysql开启url重写_开启URL伪静态的方法
- 牛客 赛码网 编程题JavaScript的输入输出
- 递归下降算法语法分析c语言
- Mac安装wget的两种方法
- 配置maven的settings文件
- 读债务危机0814-08年9月崩溃
- 【洛谷P1606】白银莲花池【最短路】
- vuepress 插件 markdown 拓展
- centos8之systemd管理服务开机自启动
- 推荐5种不错的开源虚拟化技术软件
- 数字信号处理专业术语翻译
- 2022年美赛C题-交易策略-完整解题论文和代码
- 魔力宝贝高清单机计划(一) 图库提取
- 三十八载,Oracle伴我同行—记我的职业成长之路
热门文章
- 堡垒机介绍和跳板机的简单实现
- mysql跳板机怎么搭建_Jumpserver跳板机的搭建和部署
- “钱三篇”后续之复利现值
- K3 WISE 开发插件《K3 WISE常用数据表整理》
- 54.Python的def语句自定义函数
- 互联网时代,VR全景营销的意义是什么?
- 一键打造自己的Gilde图片加载控件
- iOS开发-canOpenURL: failed for URL: mqq:// - error: This app is not allowed to query for scheme mqq
- windows环境下MySQL8.0.30的下载安装
- Swift WKWebView