多线程编程

article/2025/9/16 6:36:21

        多线程指的是一个程序中包含两个或者两个以上的线程,多线程的提出是为提高代码的执行效率,这就好比工厂中的流水线,只有一条称为单线程,有多条流水线就称为多线程。多线程提高效率的同时由于并发执行的不确定性,导致出现的结果很多是我们不想要的,所以为了得到我们想要的结果就会在编写多线程的时候加入各种锁,其中最重要的就是synchronized和volatile。在认识它们之前先从多线程最基本的开始。

目录

一:线程的创建

1.基于类继承Thread创建线程 

2.基于实现Runnable接口的方式创建线程

3.基于匿名内部类继承Thread创建线程

4.基于匿名内部类实现Runnable接口创建线程

5.基于lambda表达式创建Runnable子类对象创建线程 

6.基于线程池的方式创建多线程 

7.基于实现Callable接口的方式创建线程 

多线程中Thread的常见方法

多线程的特点

二:多线程的中断

中断一个线程有两种方式

通过共享的boolean类型的变量来中断线程

通过调用Thread里面的interrupt()方法来中断线程 

interrupt()的两个行为

三:多线程的等待

join方法

join的行为

join(long millis)方法

四:多线程的休眠

sleep的本质

五:获取多线程实例


一:线程的创建

这里有一个面试题:线程的创建方式有几种,答案一共是7中。下面讲现阶段能接触到的创建方式

1.基于类继承Thread创建线程 

public class Thread01 {public static void main(String[] args) {Mythod mythod = new Mythod();Thread thread = new Thread(mythod);thread.start();}
}
class Mythod extends Thread{@Overridepublic void run() {while(true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("你好");}}
}

2.基于实现Runnable接口的方式创建线程

public class Thread02 {public static void main(String[] args) {t t = new t();Thread thread = new Thread(t);thread.start();}
}
class t implements Runnable{@Overridepublic void run() {while(true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("再将");}}
}

3.基于匿名内部类继承Thread创建线程

public class Thread03 {public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {while(true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("背景");}}};thread.start();}
}

4.基于匿名内部类实现Runnable接口创建线程

public class Thread04 {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while(true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("李华");}}}).start();}
}

5.基于lambda表达式创建Runnable子类对象创建线程 

public class Thread05 {public static void main(String[] args) {new Thread(()->{while(true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("韩梅梅");}}).start();}
}

6.基于线程池的方式创建多线程 

public class Thread28 {public static void main(String[] args) {//使用以下标准库中的线程池//先创建一个线程池的实例ExecutorService service = Executors.newFixedThreadPool(10);//给实例里面加入一些任务for (int i = 0; i < 10; i++) {service.submit(new Runnable() {@Overridepublic void run() {System.out.println("你好");}});}}
}

7.基于实现Callable接口的方式创建线程 

public class Callable2 {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 5000; i++) {sum++;}return sum;}};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();int ret = futureTask.get();System.out.println(ret);}
}

多线程中Thread的常见方法

getName()                        获得线程的名字(在创建线程的时候可以给线程起名字)
isDaemon()                       判断线程是否为守护线程(判断是否为后台线程)
isInterrupted                    判断线程是否被中断
public class Thread06 {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {}}, "我的线程");thread.start();System.out.println(thread.getName());System.out.println(thread.isDaemon());System.out.println(thread.isInterrupted());}
}

setDaemon()                      将线程设置为后台线程

一个线程被创建出来默认就是前台线程,前台线程会阻止进程的结束,而后台线程则不会,后台线程一结束进程就结束了,因此可以将前台线程设置为后台线程,从而提高程序的执行速度。

public class Thread06 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("你好");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});thread.start();System.out.println("程序已经结束");}
}

这里先出的“程序已经结束”再输出的“你好”体现出来多线程并发执行的不确定性,这个不确定性和操作系统的实现相关。可以看到进程并没有结束,原因是应为thread是一个前台线程,进程只有等待所有前台线程都结束以后才会结束。为了结束进程,只需要在开启thread线程之前将它设置为后台线程即可。

        thread.setDaemon(true);

 

多线程的特点

1.每个线程都是一个单独的执行流

2.每个线程之间是“并发”执行的,每个线程之间的执行顺序和操作系统的具体实现有关

3.多个线程执行的时间不是一个线程的1/n的原因是因为:创建线程自身也是有开销的,多个线程cpu上不一定是纯并行,也可能并发执行 

面试题:请分别从方法及运行结果说明Thread类中run和start的区别 

  1. 方法的区别:直接调用run方法,并没有创建新的线程,而只是之前的线程中(主线程),执行了run里面的内容;使用start则是创建了新的线程,新的线程里面调用的run方法。
  2. 运行结果的区别:如果直接运行run方法,程序会等待run方法(方法里面写了个死循环)执行完毕以后才会执行其他线程,但是运行start则是线程之间的并发执行,输出的结果也是交替出现的。
  3. run方法可以多次调用,而start方法只能被调用一次
class MyThread extends Thread{@Overridepublic void run() {while(true){System.out.println("再见");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Thread01 {public static void main(String[] args) {MyThread thread = new MyThread();thread.start();while(true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("你好");}}
}

 运行的结果:

class MyThread extends Thread{@Overridepublic void run() {while(true){System.out.println("再见");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Thread01 {public static void main(String[] args) {MyThread thread = new MyThread();thread.run();while(true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("你好");}}
}

 运行结果:

二:多线程的中断

中断一个线程有两种方式

通过共享的boolean类型的变量来中断线程

中断线程就是让线程尽快将入口方法执行结束(入口方法:继承Thread重写run、实现Runnable接口重写run) 

public class Thread07 {private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(){@Overridepublic void run() {while(flag){try {System.out.println("你好");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};thread.start();Thread.sleep(3000);flag = false;System.out.println("线程已经被中断");}
}

通过调用Thread里面的interrupt()方法来中断线程 

public class Thread08 {public static void main(String args[]) throws InterruptedException {Thread thread = new Thread(){@Overridepublic void run(){while(!Thread.currentThread().isInterrupted()){try {System.out.println("你好");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};thread.start();Thread.sleep(3000);thread.interrupt();System.out.println("线程被中断");}
}

 

在分析结果之前,先明白下面d的方法:

Thread.currentThread().isInterrupted()方法

上面的这个方法是Thread类里面的静态方法,Thread.currentThread()表示获取当前线程的实例也就是线程对象。isInterrupted()方法为修改线程内置的标志位,它默认的结果是false,因此在while里面使用时注意取反。

interrupt()的两个行为

1. 如果thread线程没有处于阻塞状态,此时的interrupt就会修改内置的标志位

2.如果thread线程正处于阻塞状态,此时的interrupt就让线程内部产生阻塞的方法,在这个例子中就是sleep方法抛出异常

因此我们看到的结果首先是thread里面执行了三秒,然后调用interrupt方法,此时thread线程刚好处于阻塞状态,因此就让sleep方法抛出异常。但是由于catch语句里面并没有退出的逻辑,所以thread还会一直执行

为了看到interrupt()方法的打断效果,有两种方式:

第一种就是不要interrupt()方法前面的休眠操作,因为多线程是抢占式的执行,一旦让interrupt()方法成功将while里面的标志位变为false则thread就退出了。

public class Thread {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while(!Thread.currentThread().isInterrupted()){//isInterrupted()本身是falseSystem.out.println("线程运行中....");}}});thread.start();System.out.println("线程中断");thread.interrupt();}
}

第二种就是在catch代码块中写退出的逻辑

第二种方式是我们主要使用的,因为我们可以在catch代码块中写相应的逻辑语句,可以让thread线程立即退出,也可以让它待会退出或者是永远不退出。 

三:多线程的等待

因为多线程的执行顺序是不确定的,为了让某一个线程有明确的的执行顺序,这里可以使用线程等待机制。

join方法

join方法主要用来等待线程结束

现在要求t1线程先执行,然后t2线程在执行:看如下代码

public class Thread09 {public static void main(String[] args) {System.out.println("main_beg");Thread t1 = new Thread(()->{System.out.println("t1_beg");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1_end");});Thread t2 = new Thread(()->{System.out.println("t2_beg");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t2_end");});t1.start();t2.start();System.out.println("main_end");}
}

可以看出代码的执行顺序是乱的,这样的结果往往是我们在编程中不想要的,我们作为编程者,需要的就是确定的结果。

为了获得准确的结果就要引入join,方法就是分别在t1.start()和t2.start()下面加入t1.join()、t2.join()

join的行为

1.如果被等待的线程还没执行完就阻塞等待

2.如果被等待的线程已经执行完了,直接就返回

3.main的阻塞等待时间是所有线程执行时间的总和

join(long millis)方法

和join一样,只不过这是有时间的等待,等不到就直接走了。

四:多线程的休眠

这个也已经在前面多次看到了,方法就就不讲了,主要讲一下sleep的本质。

sleep的本质

和sleep一样的让线程等待的方法还有wait/join/等待锁...它们的本质是把线程在PCB中的队列从就绪状态移到阻塞状态。时间到了或者出发了中断就会有回到就绪队列。

五:获取多线程实例

Thread.currentThread获得当前线程的实例

可以应用到下面三种形式的线程中:

public class Thread10 {public static void main(String[] args) {Thread thread = new Thread(){@Overridepublic void run(){System.out.println(Thread.currentThread().getId());System.out.println(this.getId());}};thread.start();}
}

结果都是12。如果是继承Thread方式创建的继承,Thread.currentThread获得的对象实例就是当前的对象thread,因此可以用this来代替

但是利用实现Runnable接口和创建lamdba的方式创建线程就不能用this,this得到的不是当前对象的实例。


http://chatgpt.dhexx.cn/article/pIQ8kRi8.shtml

相关文章

两种 C++ 多线程编程方式,看完不懂打我...

多线程在实际编程中的重要性不言而喻&#xff0c;对 C 而言&#xff0c;当我们需要使用多线程时&#xff0c;有多种方案可供选择。比如 POSIX 线程 pthread、boost::thread 库、C11 开始支持的 std::thread 库&#xff0c;以及其他一些第三方库 libdispatch&#xff08;GCD&…

ARP攻击模拟工具

以下是本人平时的一些小作品&#xff0c;特此和大家一起分享... ARP攻击模拟工具 第一代 说明&#xff1a; 实现模拟以太网的ARP欺骗攻击和IP地址冲突攻击。 此为本人早期作品&#xff0c;固比较简陋和不完善&#xff0c;漏洞及错误之处可能较多请多多包涵&#xff01; 开发环…

ARP断网攻击

在之前的文章当中&#xff0c;我已经向大家介绍了关于ARP的欺骗原理。再进一步&#xff0c;将其中的原理运用到实战中去&#xff0c;又会有什么效果呢&#xff1f;ARP又可以给我们带来哪些具体的作用呢&#xff1f;毕竟实践要来支撑&#xff0c;我们之前学过的ARP欺骗原理。 我…

ARP-欺骗攻击

测试环境&#xff08;kali:192.168.189.7 win7:192.168.189.6 网关:192.168.189.2&#xff09; arpspoof -i eth0 -t 192.168.189.6 192.168.189.2 &#xff08;目标地址网关地址&#xff09; 接收获取的信息 随意登陆一个网站&#xff0c;可发现用户名及密码已被获取。可以…

ARP攻击原理及解决方法,很实用

故障原因】 局域网内有人使用ARP欺骗的木马程序&#xff08;比如&#xff1a;传奇盗号的软件&#xff0c;某些传奇外挂中也被恶意加载了此程序&#xff09;。 【故障原理】 要了解故障原理&#xff0c;我们先来了解一下ARP协议。 在局域网中&#xff0c;通过ARP协议来完成I…

ARP攻击怎么解决最安全

ARP攻击是指攻击者通过伪造网络中的ARP协议数据包&#xff0c;欺骗其他计算机的网络通信&#xff0c;从而实现中间人攻击等目的。 下面是ARP攻击的解决方法&#xff1a; 静态ARP表绑定MAC地址&#xff1a;在网络管理员的控制下&#xff0c;将主机的IP地址与MAC地址进行绑定&am…

ARP攻击及原理

ARP攻击原理:但凡局域网内存在arp攻击&#xff0c;说明网络存在“中间人” 1.PC1需要跟PC2通信&#xff0c;通过ARP请求包询问PC2的MAC地址&#xff0c;由于采用广播的形式&#xff0c;所以交换机会将ARP请求包从接口PC1广播到PC2和PC3。(注&#xff1a;交换机收到广播/组播/未…

ARP攻击、欺骗及防御

一、广播与广播域概述 1、广播与广播域 广播&#xff1a;将广播地址做为目的地址的数据帧。 广播域&#xff1a;网络中能接收到同一个广播所有节点的集合。 2、MAC地址广播 广播地址为FF-FF-FF-FF-FF-FF 3、IP地址广播 1&#xff09;255.255.255.255 2&#xff09;广播…

ARP攻击原理和kali实现ARP攻击

目录 一、ARP协议和ARP攻击1.ARP协议2.利用Wireshark分析ARP数据包3.ARP攻击 二、kali实现ARP攻击和ARP欺骗1.实验过程2.问题记录T_T 一、ARP协议和ARP攻击 1.ARP协议 ARP协议&#xff0c;地址解析协议&#xff08;Address Resolution Protocol&#xff09;&#xff0c;用来实…

怎么处理ARP攻击

ARP故障说明: ping 的时候可能好久才能接收到一个响应&#xff0c;或者说干脆就Ping不通。网络时好时坏&#xff0c;通过ARP -a命令不能看到同网段的在线用户&#xff0c;但是net view 可以看到全网开放共享的电脑名&#xff0c;这时候就需要注意了&#xff0c;可能是ARP攻击。…

网络安全--ARP攻击原理与防护

目录 一.ARP的原理 二.ARP攻击现象及危害 三.ARP攻击的原理 四.ARP防护 在局域网当中&#xff0c;有一个协议由于它的特性一旦遭受攻击就非常麻烦。首先是它的攻击门槛比较低&#xff0c;找到一些小工具就能实现攻击&#xff0c;而且危害极大。这个协议就是ARP协议。 ARP攻击是…

渗透技术——ARP攻击

Part 1: ARP攻击介绍 ARP&#xff08;Address Resolution Protocol&#xff0c;地址解析协议&#xff09;是一个位于TCP/IP协议栈中的底层协议&#xff0c;负责将某个IP地址解析成对应的MAC地址。而ARP攻击就是通过伪造IP地址和MAC地址实现ARP欺骗&#xff0c;能够在网络中产生…

Linux操作系统及其发行版本

文章目录 Linux是什么Liunx与WindowsLinux的发行版本1. Debian系列&#xff1a;2. Slackware系列&#xff1a;3. Redhat系列&#xff1a;4. 其他发行版本&#xff1a; Linux是什么 Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户、…

linux查看系统版本命令

*一、查看Linux系统发行版本 * 命令1&#xff1a;lsb_release -a 该命令适用于所有Linux系统&#xff0c;会显示出完整的版本信息&#xff0c;包括Linux系统的名称&#xff0c;如Debian、Ubuntu、CentOS等&#xff0c;和对应的版本号&#xff0c;以及该版本的代号&#xff0c…

哪些是linux系统版本,linux系统主要有哪些版本

linux系统主要版本有&#xff1a;Redhat版本&#xff0c;基于RPM包的YUM包管理方式&#xff1b;2.CentOS版本&#xff0c;免费的、开源的、可以重新分发的linux发行版&#xff1b;3.Ubuntu版本&#xff0c;拥有漂亮的用户界面的系统&#xff1b;4.Mandriva版本&#xff0c;KDE桌…

Linux操作系统介绍及版本

1.1 认识Linux Linux操作系统是基于UNIX以网络为核心的设计思想&#xff0c;是一个性能稳定的多用户网络操作系统&#xff0c;Linux能运行各种工具软件、应用程序以及网络协议&#xff0c;它支持安装在32位和64位CPU硬件上。 通常来讲&#xff0c;Linux这个词只表示Linux内核&a…

Linux版本简介

Linux系统最早由LinusTorvalds在1991年开始编写&#xff0c;在此诞生之前同时也具备了五大前提条件&#xff1a;UNIX操作系统、MINIX 操作系统、GNU计划、POSIX标准、INTERNET。先让我们看一下Linux系统的发行版本&#xff0c;包括Redhat、Ubuntu、Fedora、SUSE、Slackware、De…

Linux各个系统版本及区别(Ubuntu,CentOS..)

一、概览 Linux的版本号分为两部分&#xff1a;内核版本和发行版本。 内核版本&#xff1a;不包含外围程序GNU程序库和工具&#xff0c;命令行shell&#xff0c;图形界面的X Window系统和相应的桌面环境等 发行版本&#xff1a;RedHatLinux&#xff0c;CentOS, Ubuntu&#xff…

网站整合CKEditor和CKFinder(Java版)

准备文件 CKEditor: 在 http://ckeditor.com/download &#xff0c;可以下载到各种版本的CKEditor&#xff0c;包括完整版full、标准版standard、基础版basic等。同时也可定制的下载&#xff0c;可以选择Toolbar类型、插件、语言等。 CKEditor for java: 在http://ckeditor.c…

编辑组件CKEditor与文件管理组件CKFinder

ckeditor ckfinder ckeditor:html文本编辑插件&#xff0c;源码为javascript编写,实现对文本域的托管 下载时选择Full Package版本 解压后,去除一些不必要的文档;放置到js目录下即可 当然,也可以看看搭建案例samples 在这里可以设置需要的组件功能按钮 勾选完成后;得到配置代…