目录
指在一个程序中同时运行多个线程,每个线程可以执行不同的任务。多线程可以提高程序的性能和响应速度,充分利用计算机的多核处理能力。同时,多线程也可以实现任务的并发执行,提升程序的效率。多线程之间可以共享数据,但也需要注意线程安全的问题。
指一个内存中运行的应用程序,每一个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程,进程也是程序的一次执行过程,是系统运行程序的基本单位,系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
注:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
指java程序只有一个线程(main方法的线程),执行从main方法开始,从上到下依次执行,执行过程如下:
场景:
模拟王者游戏中,两组英雄进行攻击(米莱迪 攻击 诸葛亮 and 亚瑟 攻击 猪八戒),下面是采用单线程的方式实现。
Hero.java(角色类)
package com.zhy.multiplethread;
public class Hero{
/**
* 角色名称
*/
public String name;
/**
* 角色血量
*/
public float hp;
/**
* 攻击伤害
*/
public int damage;
public Hero(){
}
public Hero(String name,float hp,int damage){
this.name = name;
this.hp = hp;
this.damage = damage;
}
/**
* 角色攻击方法
* @param h
*/
public void attackHero(Hero h) {
try {
//攻击需要时间,每次攻击暂停1000毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每次攻击,角色的血量逐渐减少
h.hp -= damage;
System.out.format("%s 正在攻击 %s, %s 的血变成了 %.0f%n",name,h.name,h.name,h.hp);
if(h.isDead()){
System.out.println(h.name +" 已被击杀!");
}
}
/**
* 判断角色是否已被击杀(根据血量判断),血量>=0:返回true,否则:返回false
* @return
*/
public boolean isDead() {
return 0 >= hp ? true : false;
}
}
TestThread.java(测试类)
package com.zhy.multiplethread;
public class TestThread {
public static void main(String[] args) {
Hero hero1 = new Hero("米莱迪",350,60);
Hero hero2 = new Hero("诸葛亮",300,50);
Hero hero3 = new Hero("亚瑟",450,70);
Hero hero4 = new Hero("猪八戒",400,60);
//米莱迪 攻击 诸葛亮
while(!hero2.isDead()){
hero1.attackHero(hero2);
}
//亚瑟 攻击 猪八戒
while(!hero4.isDead()){
hero3.attackHero(hero4);
}
}
}
输出结果:
使用单线程实现,可以看出,第二组英雄 是在 第一组英雄攻击完毕后 才开始攻击的。
创建多线程的常用方式有三种,第一种是继承Thread类;第二种是实现Runnable接口;第三种是直接使用匿名内部类进行具体实现。下面我们依次进行详细解析。
java.lang.Thread类:Java线程模型的基础,只需要继承Thread类并重写run()方法来定义线程的执行逻辑。然后可以通过创建Thread的实例来启动线程。
实现步骤:
- 创建一个Thread类的子类。
- 在Thread类的子类中重写Thread类中的run()方法,设置线程任务(具体业务场景)。
- 创建Thread类的子类对象为实例。
- 调用Thread类中的start方法,开启新的线程,实际是执行的run()方法。
注意事项:
- void start():使该线程开始执行;java虚拟机调用该线程的run()方法。
- 启动线程后,实际是两个线程并发的运行,当前线程(main线程)和另一个线程(创建的新线程,执行其run()方法)。
- 多次启动一个线程是非法的,特别是当线程已经结束执行后,不能在重新启动。
代码示例:
场景:
模拟王者游戏中,两组英雄进行攻击(米莱迪 攻击 诸葛亮 and 亚瑟 攻击 猪八戒),下面是采用多线程的方式实现。
KillThread.java(Thread的子类)
package com.zhy.multiplethread;
/**
* 1.创建一个Thread类的子类。
*/
public class KillThread extends Thread{
private Hero h1;
private Hero h2;
public KillThread(Hero h1, Hero h2){
this.h1 = h1;
this.h2 = h2;
}
/**
* 2.在子类中重写Thread类中的run()方法,设置线程任务。
* 当角色h2没有被击杀时,角色h1持续进行攻击
*/
public void run(){
while(!h2.isDead()){
h1.attackHero(h2);
}
}
}
TestThread.java(测试类)
package com.zhy.multiplethread;
public class TestThread {
public static void main(String[] args) {
Hero hero1 = new Hero("米莱迪",350,60);
Hero hero2 = new Hero("诸葛亮",300,50);
Hero hero3 = new Hero("亚瑟",450,70);
Hero hero4 = new Hero("猪八戒",400,60);
//3.创建Thread类的子类对象
KillThread killThread1 = new KillThread(hero1,hero2);
KillThread killThread2 = new KillThread(hero3,hero4);
//4.调用Thread类中的start方法,开启新的线程,实际是执行的run()方法。
killThread1.start();
killThread2.start();
}
}
输出结果:
java程序属于抢占式调度,哪个线程的优先级高,哪个线程就先执行,同一个优先级,随机选择一个执行。从输出中可以看到,第一组英雄 还没攻击完毕 第二组英雄就开始攻击了,可以很大程度的提高程序的性能和速度。
java.lang.Runnable接口:实现Runnable接口的类可以作为Thread类的构造函数参数来创建线程。这种方式更加灵活,因为Java不支持多继承,所以实现Runnable接口可以使类在同时继承其他类的情况下创建线程。
构造方法:
Thread(Runnable target):分配新的Thread对象。
Thread(Runnable target,String name):分配新的Thread对象。
实现步骤:
- 创建一个Runnable接口的实现类。
- 在实现类中重写Runnable接口的run方法,设置线程任务。
- 创建一个Runnable接口的实现类对象。
- 创建Thread类对象,构造方法中传递Runnable接口的实现类对象。
- 调用Thread类中的start方法,开启新的线程执行run方法。
代码示例:
场景同上
Battle.java(Runnable的实现类)
package com.zhy.multiplethread;
/**
* 1.创建一个Runnable接口的实现类
*/
public class Battle implements Runnable{
private Hero h1;
private Hero h2;
public Battle(Hero h1, Hero h2){
this.h1 = h1;
this.h2 = h2;
}
/**
* 2.在实现类中重写Runnable接口的run方法,设置线程任务
*/
public void run(){
while(!h2.isDead()){
h1.attackHero(h2);
}
}
}
TestThread.java(测试类)
package com.zhy.multiplethread;
public class TestThread {
public static void main(String[] args) {
Hero hero1 = new Hero("米莱迪",350,60);
Hero hero2 = new Hero("诸葛亮",300,50);
Hero hero3 = new Hero("亚瑟",450,70);
Hero hero4 = new Hero("猪八戒",400,60);
//3.创建一个Runnable接口的实现类对象
Battle battle1 = new Battle(hero1,hero2);
Battle battle2 = new Battle(hero3,hero4);
//4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象;调用Thread类中的start方法,开启新的线程执行run方法
new Thread(battle1).start();
new Thread(battle2).start();
}
}
实现Runnable接口创建多线程程序的好处:
格式:
new 父类/接口(){
重写父类/接口中的方法
};
作用:简化代码
实现方式:
- 把(子类继承父类,重写父类的方法,创建子类对象)合一步完成。
- 把(实现类实现接口,重写接口中的方法,创建实现类对象)合成一步完成。
- 匿名内部类的最终产物:子类/实现类对象,而这个类没有名字。
代码示例:
场景同上
TestThread.java(测试类)
package com.zhy.multiplethread;
public class TestThread {
public static void main(String[] args) {
Hero hero1 = new Hero("米莱迪",350,60);
Hero hero2 = new Hero("诸葛亮",300,50);
Hero hero3 = new Hero("亚瑟",450,70);
Hero hero4 = new Hero("猪八戒",400,60);
//匿名类:线程的父类是Thread
new Thread(){
public void run(){
while(!hero2.isDead()){
hero1.attackHero(hero2);
}
}
}.start();
//匿名类:线程的接口是Runnable
new Thread(new Runnable() {
@Override
public void run() {
while(!hero4.isDead()){
hero3.attackHero(hero4);
}
}
}).start();
}
}
场景:模拟 秒针 的转动,每隔一秒执行依次。
package com.zhy.thread;
//继承自Thread类
public class ThreadTest extends Thread{
//定义一个无参构造方法
public ThreadTest(){
}
//定义一个有参构造方法
public ThreadTest(String name){
super(name);
}
//重写父类中的run方法
@Override
public void run() {
//获取当前正在执行的线程对象
Thread thread = Thread.currentThread();
//获取当前线程的名称
String threadName = thread.getName();
System.out.println(threadName);
//也可以直接分成一步实现,采用链式编程
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
//启动多线程1
ThreadTest threadTest = new ThreadTest();
threadTest.setName("xiaoQiang");
threadTest.start();
//启动多线程2
new ThreadTest("wancai").start();
//模拟秒针,每隔一秒执行一次
for (int i = 1; i <= 60; i++){
System.out.println(i);
try {
//该方法是一个编译时异常,必须处理
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf