JavaEE-多线程初阶1

news2024/9/22 17:33:23

✏️作者:银河罐头
📋系列专栏:JavaEE

🌲“种一棵树最好的时间是十年前,其次是现在”

目录

  • 1.认识线程
    • 1.1概念
    • 1.2多线程程序
    • 1.3创建线程
  • 2.Thread类及常见方法
    • 2.1Thread 的常见构造方法
    • 2.2 Thread 的几个常见属性
    • 2.3中断一个线程
    • 2.4等待一个线程-join()

1.认识线程

1.1概念

进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位

引入进程这个概念,最主要的目的是为了解决"并发编程"这样的问题,这是因为CPU进入了多核心时代,要想进一步的提升程序的执行速度,就需要充分的利用CPU的多核资源。

其实,多进程编程,已经可以解决并发编程的问题了,已经可以利用起来CPU的多核资源了。

但是,进程太重了。(这里的"重"指的是消耗资源多+速度慢)

进程"重"就"重"在了资源分配/回收

创建一个进程,开销比较大;销毁一个进程,开销也比较大;调度一个进程,开销还比较大。

线程也叫做"轻量型进程"。解决并发编程的问题的前提下,让创建,销毁,调度的速度更快一些。

线程为什么更"轻",因为它省去了申请资源和释放资源的操作。

举例:一个工厂加工产品

image-20221201123811467

工厂效益好了,想要提高生产力,有两个方案

方案一:再租一个场地,再买一套生产线和物流线

image-20221201123937977

方案二:在原场地的基础上,只用买一套生产线,场地和物流线可以复用

image-20221201124130935

分析:方案二与方案一相比更节省成本

方案一相当于多进程,方案二相当于多线程

线程和进程的关系,进程包含线程。

一个进程可以包含1个或多个线程,不能没有。

只有第一个线程启动的时候开销是比较大的,后续线程就省事了。

同一个进程的多个线程之间共用同一份资源(主要指的是内存和文件描述符表)。(这里的内存:你线程1new的个对象,线程2,3,4都可以直接使用)(这里的文件描述符表:线程1里打开的文件,线程2,3,4都可以直接使用)

操作系统里实际调度的时候,是以线程为单位进行调度的。

如果每个进程有多个线程,那么每个线程是独立在CPU上进行调度的,线程是操作系统调度执行的基本单位。

每个线程有自己的执行逻辑(执行流)

一个线程也是通过一个PCB进行描述的,也就是说一个进程可能对应1个PCB,也可能对应多个PCB。

之前学过的,PCB里的状态,上下文,优先级,记账信息都是每个线程有自己的,各自记录各自的。

而同一个进程的PCB之间,pid是一样的,内存指针,文件描述符表也是一样的。

进程专门负责资源分配,线程来接管和调度相关的一切内容。

多线程也会带来一些问题:

  • 为了提高速度可以增加线程数量,但不是一直增加线程数量,速度能一直提高的,因为CPU核心数量是有限的。线程数目太多,核心数目有限,不少的开销反而浪费在线程调度上了。

系统创建线程,也是要消耗资源的,(虽然比进程轻量,但也不是0),如果你创建线程太多,会导致资源耗尽,导致别的进程用不了,这里的资源指的是CPU,内存,带宽等。

  • 多线程情况下,还可能带来线程安全问题。

    在多个执行流访问同一个共享资源的时候可能会有线程安全问题。

    线程模型,天然就是资源共享的,多线程争抢同一个资源(同一个变量)非常容易触发。

    进程模型,天然是资源隔离的,不容易触发。在进行进程间通信的时候,多个进程访问同一份资源,可能会有问题。

  • 如果一个线程抛异常,如果没有处理好,可能使得整个进程崩了,其他线程也就挂了。

chrome浏览器要使用多进程编程模型(每个标签页都是一个进程),目的是为了防止一个页面挂了使得其他页面也挂了

多线程的编程模型要比多进程的编程模型更加广泛

1.2多线程程序

在Java中如何是实现多线程编程。

Java执行多线程,最核心的类Thread

Thread t = new MyThread();
t.run();//虽然t是父类引用,此处调用的run仍然是子类的方法。(t本质上还是指向子类的对象)

使用Thread类不需要import别的包,Thread类在java.lang包下

还有哪些类类似?String,StringBuffer,StringBuilder

而像ArrayList,HashMap,Scanner,Random这些类都在java.util包下

创建线程是希望线程能够成为一个独立的执行流(执行一段代码)

如何指定?

t.start();
//t创建了一个新的线程,由这个新的线程调用run()
//调用操作系统的API,通过操作系统内核创建新线程的PCB,并且把要执行的指令交给这个PCB,然后这个PCB被调度到CPU上执行的时候,就执行到了线程run方法里的代码
public static void main(String[] args) {
        System.out.println("hello world");
    //如果只是直接打印hello world,Java进程主要就是有一个线程(调用main方法的线程),主线程
    }
package Thread;

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello world");
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();//不创建线程(说的线程指的是系统内核里的PCB)
        t.start();
        //通过t.start(),主线程调用t.start(),创建一个新线程,通过这个新线程调用t.run()
        //如果run()方法执行完毕,新线程就销毁
    }
}

怎样体现出并发编程?

package Thread;

class MyThread extends Thread{
    @Override
    public void run() {
        while(true) {
            System.out.println("hello thread");
            //为了让这里的打印慢点,方便看,加个sleep,休眠1s
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
        while(true){
            System.out.println("hello main");
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

操作系统调度线程的时候,“抢占式执行”,具体哪个线程先上,哪个线程后上,不确定,取决于操作系统调度器具体实现策略。

虽然有优先级,但是在应用程序(代码)层面上无法修改,从应用程序(代码)的角度,看到的效果就好像是线程之间的调度顺序是"随机"的一样。

内核里并非是随机的,但是干预因素太多了,并且应用程序这一层也无法感知到细节,就只能认为是随机的了。(可以通过一些api进行有限度的干预)

  • start和run之间的区别:start是真正创建了一个线程(从系统这里创建的),线程是独立的执行流。

可以使用jdk自带的工具jconsole查看当前的java进程中的所有线程image-20221202164440434image-20221202164206774JDK是Java开发工具包,这里带的工具很多,不只是javac和javaimage-20221202164736644image-20221202170005758

main线程销毁之后,thread-0线程会销毁吗?默认是不会的,守护线程/非守护线程

PCB对应的是线程,一个线程对应一个PCB,一个进程对应多个PCB,如果一个进程里只有一个线程,那就是一个进程对应一个PCB了。

同一个进程里的若干PCB的pid相同,不同进程里的pid不同

PCB只是操作系统书里说的概念,实际上Linux对应结构体名字叫做task_struct

PCB不是简称,它是一个数据结构,体现的是进程/线程是如何实现,如何被描述出来的。

image-20221202191626203

1.3创建线程

  • 1.继承Thread,重写run
package Thread;
class MyThread extends Thread{
    @Override
    public void run() {
        while(true) {
            System.out.println("hello thread");
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
        while(true){
            System.out.println("hello main");
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
  • 2.实现Runnable接口
package Thread;
//Runnable作用是描述一个要执行的任务,run方法就是任务的执行细节
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread t = new Thread(runnable);
        t.start();
    }
}
//解耦合,让线程和线程要执行的任务分离开
//好处是如果未来要改代码,不用多线程,使用多进程,或者线程池,协程...代码改动比较小
  • 3.使用匿名内部类,继承Thread
//使用匿名内部类来创建线程
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(){
            //1.创建了一个Thread类的子类,(子类没有名字所以才叫匿名)
            //2.创建了子类的实例并且让t指向该实例
            @Override
            public void run() {
                System.out.println("hello");
            }
        };
        t.start();
    }
}
  • 4.使用匿名内部类,实现Runnable
public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
    }
}
//这个写法和2本质相同,只不过是把实现Runnable的任务交给匿名内部类的语法
//此处是创建了一个类,实现了Runnable接口,并创建了类的实例,并且传给了Thread的构造方法。
  • 5.使用Lambda表达式
public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello");
        });
        t.start();
    }
}
//把任务用lambda表达式来描述,直接把lambda传给Thread的构造方法
//lambda就是个匿名函数,就用一次

2.Thread类及常见方法

2.1Thread 的常见构造方法

image-20221202211051757

package Thread;

public class ThreadDemo6 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("hello");
                }
            }
        },"myThread");
        t.start();
        //对于主线程来说,执行完t.start()之后,main方法结束,主线程执行完了
        //t是代码里的变量名,而myThread是系统里的线程名
    }
}

image-20221202211208211

注意这里线程列表中没有主线程,因为主线程执行完了

2.2 Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • getName():构造方法里起的名字

  • getState():线程状态。Java里线程的状态要比操作系统原生的状态更加丰富一些

  • getPriority():这个可以获取,也可以设置(但设置没什么用)

  • isDaemon():是否是守护线程(后台线程)。前台线程会阻止进程结束,后台线程不会阻止进程结束

代码里手动创建的线程默认都是前台的,包括main默认也是前台的,其他JVM自带的线程是后台的

也可以手动的使用setDaemon设置成后台线程

image-20221202214759709

  • isAlive()

    image-20221203095811771

    在真正调用start之前,调用t.isAlive是false,调用t.start之后isAlive就是true。另外如果内核里线程把run执行完了,此时线程销毁PCB随之释放,但是Thread t这个对象还不一定被释放,此时isAlive是false。

    isAlive是在判断当前系统里这个线程是否存在

public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                    System.out.println("hello");
            }
        },"myThread");
        t.start();//t还在,引用不指向这个对象被GC回收的时候,t就不在了
        while(true){
            try{
                Thread.sleep(1000);
                System.out.println(t.isAlive());
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

如果t.run还没执行,isAlive是false;

如果t.run正在执行,isAlive是true;

如果t.run执行结束,isAlive是false;

2.3中断一个线程

中断的意思不是让线程立即停止,而是通知线程你该停止了。是否真的停止,取决于线程这里的代码写法。

  • 1.使用标志位来控制线程是否要停止
private static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(flag){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        //在主线程里就可以随时通过flag变量的取值,来操作t线程是否结束
        flag = false;
    }
  • 2.使用Thread自带的标志位来进行判断

这种可以唤醒sleep

public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                //Thread.currentThread()
                //这是Thread类的静态方法,通过这个方法可以获取到当前线程,哪个线程调用的这个方法,就可以获取到这个线程的对象引用,相当于this
                //是在t.run中被调用的,此处获取到的就是t线程
                //isInterrupted()为true表示被终止
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }
//如果线程在sleep中休眠,此时调用interrupt就会把t线程唤醒,从sleep提前返回。interrupt会触发sleep里的异常InterruptedException,导致sleep提前返回

main调用t.interrupt(),main通知t要被终止

//结果:
hello thread
hello thread
hello thread
//t正常执行
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at Thread.ThreadDemo9.lambda$main$0(ThreadDemo9.java:16)
	at java.lang.Thread.run(Thread.java:748)
//调用interrupt触发了异常
hello thread
hello thread
hello thread
hello thread
hello thread
hello thread
.
.
.
//t还在继续执行

interrupt:

把线程内部的标志位(boolean)设置成true,

如果线程在sleep,就会触发异常把sleep唤醒,同时把刚才设置的这个标志位设置成false(清空了标志位),这就导致当sleep的异常被catch完了之后,循环还要继续执行

//线程t忽略终止请求
public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }
//线程t立即响应你的终止请求
public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }
//稍后进行终止
public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }

如果不是sleep,像wait,join等类似的造成代码"暂停"的方法都会有清除标志位的设定

2.4等待一个线程-join()

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。等待线程,控制两个线程的结束顺序。

public static void main(String[] args) {
        Thread t = new Thread(()->{
            for(int i = 0;i < 3;i++){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });
        t.start();
        System.out.println("join 之前");
        //此处的 join 就是让当前的 main 线程来等待 t 线程执行结束(等待 t 的 run 执行完)
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join 之后");
    }

执行完t.start()之后main线程和t线程就并发执行。main继续执行遇到join发生阻塞(block),一直阻塞到t线程结束,main线程才从join中恢复回来然后继续向下执行。t线程比main线程先结束

//结果:
join 之前
hello thread
hello thread
hello thread
join 之后
  • 如果执行join时,t线程已经结束,会发生什么?
public static void main(String[] args){
        Thread t = new Thread(()->{
            for(int i = 0;i < 3;i++){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });
        t.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join 之前");
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join 之后");
    }
//结果:
hello thread
hello thread
hello thread
join 之前
join 之后

如果 join 时 t 线程已经结束,join就不会阻塞,就会立即返回。

image-20221203162405393

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在本地利用服务器显卡跑代码

除了使用xshell等连接服务器以外&#xff0c;pycharm也可以连接服务器&#xff0c;在服务器上运行代码&#xff0c;上传下载文件等操作。 参考&#xff1a;https://cloud.tencent.com/developer/article/1738482 步骤如下&#xff1a; 1、pycharm工具栏&#xff1a;Tools– D…

基于51单片机的压力监测仪(MPX4115)(Proteus仿真+程序)

编号&#xff1a;28 基于51单片机的压力监测仪(MPX4115) 功能描述&#xff1a; 本设计由51单片机最小系统MPX4115压力传感器ADC0832模块液晶1602模块 1、主控制器是AT89C82单片机 2、MPX4115压力传感器采集气压力&#xff0c;通过ADC0832模数转换器进行A/D转换&#xff0c;读…

Java语言与系统设计课程实验报告

做个课设做的我人间失格&#xff0c;写了一晚上没保存&#xff0c;真是哭死 一、目的与要求 &#xff08;一&#xff09;、实验目的 掌握Java语言与系统设计的基本思路和方法。 利用所学的基本知识和技能&#xff0c;解决简单的Java语言与系统设计问题。 &#xff08;二&…

挂耳式蓝牙耳机性价比推荐,几款高性能的耳机分享

无论是在日常还是运动的场景下&#xff0c;我们通常都会选择佩戴着耳机&#xff0c;让我们能够顺利过渡掉枯燥的生活&#xff0c;之前人们会选择入耳式的耳机&#xff0c;在长期佩戴过后会有不小的疾病诞生&#xff0c;在近些年迅速火起的骨传导耳机成为了焦点&#xff0c;其保…

Java线程池理解与学习

线程过多就容易引发内存溢出&#xff0c;因此我们有必要使用线程池的技术 线程池的好处 降低资源消耗&#xff1a; 通过重复利用已创建的线程降低线程创建和销毁造成的消耗 提高响应速度&#xff1a; 当任务到达时&#xff0c;任务可以不需要等待线程创建就能立即执行 提高线…

GEE:关系、条件和布尔运算

ee.Image对象具有一组用于构建决策表达式的关系、条件和布尔运算方法。这些方法可以用来掩膜、绘制分类地图和重新赋值。 本文记录了在GEE&#xff08;Google Earth Engine&#xff09;平台上的关系运算符和布尔运算符&#xff0c;分别应用到了三个不用的场景&#xff08;筛选低…

【坚持不懈的每日一题——力扣篇】1796. 字符串中第二大的数字(简单)+set 用法复习

GitHub同步更新&#xff08;已分类&#xff09;&#xff1a;Data_Structure_And_Algorithm-Review 公众号&#xff1a;URLeisure 的复习仓库 公众号二维码见文末 以下是本篇文章正文内容&#xff0c;下面案例可供参考。 一、题目描述 力扣今天推的每日一题是道简单题&#x…

[附源码]计算机毕业设计心理健康系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

读写锁三种关系的证明(读者和读者互补影响、写者和写者互斥、读者和写者互斥)

目录 1、读者和读者互不影响 2、写者和写者互斥 3、读者和写者互斥 (1) 读者持有锁 (2) 写者持有锁 1、读者和读者互不影响 假设现在只有读者线程&#xff0c;我们让一个读者线程申请锁以后&#xff0c;但是不释放读写锁。 #include <stdio.h> #include <unist…

[附源码]JAVA毕业设计计算机专业在线学习评估软件-演示录像-(系统+LW)

[附源码]JAVA毕业设计计算机专业在线学习评估软件-演示录像-&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&…

HTML小游戏15 —— 网页版3D反恐英雄(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计…

[附源码]Python计算机毕业设计Django教学辅助系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

2022年各大企业java面试题解析,堪称全网最详细的java面试指南

前言 最近感慨面试难的人越来越多了&#xff0c;一方面是市场环境&#xff0c;更重要的一方面是企业对Java的人才要求越来越高了。 ​基本上这样感慨的分为两类人&#xff0c;第一&#xff0c;虽然挂着3、5年经验&#xff0c;但肚子里货少&#xff0c;也没啥拿得出手的项目&am…

膜拜,终于拿到了美团大佬分享的Netty源码剖析与应用PDF

前言 时间飞逝&#xff0c;转眼间毕业七年多&#xff0c;从事 Java 开发也六年了。我在想&#xff0c;也是时候将自己的 Java 整理成一套体系。 这一次的知识体系面试题涉及到 Java 知识部分、性能优化、微服务、并发编程、开源框架、分布式等多个方面的知识点。 写这一套 Ja…

机器学习笔记之受限玻尔兹曼机(二)模型表示

机器学习笔记之受限玻尔兹曼机——模型表示引言回顾&#xff1a;玻尔兹曼分布玻尔兹曼机关于玻尔兹曼机的问题受限玻尔兹曼机受限玻尔兹曼机的学习任务(填坑)引言 上一节基于马尔可夫随机场介绍了玻尔兹曼分布&#xff0c;本节将介绍受限玻尔兹曼机的模型表示(Representation)…

阿里资深专家分享程序员三门课:技术精进架构修炼、管理探秘文档

前言 学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生…

将0-255的色彩映射表导出为RGB波段

当我们拿到0-255的色彩映射表栅格文件的时候&#xff0c;可能不太好用&#xff0c;需要导出为RGB波段或者其他波段形式的&#xff0c;在Global Mapper中可以做到。 打开0-255色彩映射表的栅格文件&#xff0c;在Arcmap中可以看到是这样的&#xff1a; 1 在ArcMap中操作步骤…

PE文件硬编码代码注入

以下适合有PE基础的人看&#xff0c;最起码要知道PE的基本结构和rva以及foa之间如何相互转换&#xff0c;不然会看的迷迷糊糊 先决条件 首先我们需要准备一个程序&#xff0c;待会将代码注入这个程序中 随便编写一个简单的程序&#xff0c;将随机基址给关闭 硬编码 程序编…

PyQt5基础练习2

实验4 关闭窗口 4.1 完整代码 #!/usr/bin/python3 # -*- coding: utf-8 -*-""" ZetCode PyQt5 tutorialThis example shows a tooltip on a window and a button.Author: Jan Bodnar Website: zetcode.com Last edited: August 2017 """import…

推荐系统在腾讯游戏运营中的实践

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年10月份热门报告盘点2021-2022元宇宙报告.pdf清华大学256页PPT元宇宙研究报告.pdf&#xff08;附下载链接&#xff09;机器学习在B站推荐系统中的应用实践小红书推荐系统…