Java基础---多线程

Java 多线程编程:从基础到实战! 多线程编程是 Java 中的一个核心概念,它允许多个线程同时执行任务,从而提高程序的效率和响应性。在现代应用程序中,多线程编程无处不在,尤其是在处理高并发任务时。今天,我们将从基础到实战深入讲解 Java 的多线程编程,帮助你更好地理解和应用它。 阅读详情


多线程的概念,如何在程序中创建多线程(Thread、Runnable),线程安全问题,线程的同步,线程之间的通讯、死锁。

一、 创建线程和启动线程。
为什么要覆盖run方法:Thread类用于描述线程。
该类就定义了一个功能。用于存储线程要执行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法。用于存储线程要运行的代码。
创建线程的两种方法中,实现Ruunable接口可以避免单继承的局限性。
启动线程d1.run(); t1.start();run和start的区别:d1.run();就开始执行run方法中的代码了。
start方法是:启动线程,调用run方法。

二  线程的安全机制

线程安全问题都是由全局变量及静态变量引起的。当一个线程在运行时,
另一个线程进来并改变了当前线程运算需要用到的变量的值,这就会导致当前线程计算出错,
这就是线程的不安全。
为了提高多线程安全性,解决方法有同步代码块和同步函数。
同步的前提:
1.必须要有两个或者两个以上的线程。

2.必须是多个线程使用同一个锁。

利弊:
好处:解决了线程安全问题。
弊端:多个线程需要判断锁,较为消耗资源。

代码实例:

<span style="font-size:14px;">package Thread;

/**
 * @author Administrator
 *创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。
 *		该子类应重写 Thread 类的 run 方法。
 *
 *另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。
 *
 *
 *将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
 */
public class ThreadBandTest {

	public static void main(String[] args) {
		//第1种继承Thread,并复写run方法。
		Demo d1 = new Demo();
		Demo d2 = new Demo();
		d1.run();
		d2.run();
		
		//第2种实现Runnable接口。
		Cus c = new Cus();
		//开启两个储户线程
		Thread t1 = new Thread(c);
		Thread t2= new Thread(c);
		
		//启动线程
		t1.start();
		t2.start();
	}

}

/*
 * 同步的两种表达方式
1,同步代码块
2,同步函数
 * */
//创建银行类
class Bank{
	//储户存入总和
	private int sum;
	
	//同步函数,为两个储户线程共享的代码(数据)
	/*public synchronized void add(int n){
		sum=sum+n;
		try {
			Thread.sleep(100);
		} catch (Exception e) {
			// TODO: handle exception
					}
		System.out.println("sum= "+sum);
	}*/
	//同步代码块实现线程的代码共享
	Object obj = new Object();
	public void add(int n){
		synchronized (obj) {
			sum = sum+n;
			try {
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
			System.out.println("sum = "+sum);
		}
	}
	
}

//继承Thread
class Demo extends Thread{
	public void run(){
		for(int i=0;i<8000;i++)
			System.out.println(i);
	}
}

//创建储户类   实现Runnable接口  
class Cus implements Runnable{
	private Bank b = new Bank();
	//实现每个储户存储三次,每次100
	public void run(){
		for(int x=0;x<3;x++){
			b.add(100);
		}
	}
}
</span>

三  线程之间的通讯


线程之间通讯:其实就是多个线程在操作同一个资源。但是操作的动作不同。用到的方法如下:
wait();当前线程处于等待状态,直到有notify将其唤醒。
notify();唤醒一个线程池中持有同样锁的等待线程,通常唤醒第一个被等待的线程。
notifyall();唤醒线程池中所有的等待

这三个方法都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁

为什么这些操作线程的方法要定义在object中呢?
因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁只有同一个锁上的被等待线程,
可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒。
也就是说等待跟唤醒必须是同一把锁

而锁可以是任意对象,所以可以被任意对象调用的方法定义在object类中

示例代码:
演示两个线程操作同一个资源,但是动作不同,将两个动作的代码块都进行同步,并且使用同一把锁,
但是存在动作一或者动作二一直持有执行权和执行资格,为了实现存入和输出交替进行,加入了wait()和notify方法,
输入和输出通过判断标志flag的真假,交替进行。
但是存在同步嵌套的情况,这时同步所用的锁不同,这样就导致在执行wait和notify的时候具体等待或者唤醒哪个线程
不明确,所以要标识锁,如r.wait();   r.notify();  唤醒的是持有r这把锁的同步代码块中的线程,
1.动作一,存入信息

<span style="font-size:14px;">synchronized(r)
			{
				if(r.flag)  //初始为假  存入信息
					//存入信息线程判断true则等待被唤醒,false则执行存入
					try{r.wait();}catch(Exception e){}
				if(x==0)
				{
					r.name="mike";
					r.sex="man";
				}
				else
				{
					r.name="丽丽";
					r.sex="女女女女女";
				}
				x=(x+1)%2;
				r.flag=true;//存完以后置为真,此时存入线程判断为真,执行等待。
				r.notify();//唤醒输出信息线程
			}</span>

2.动作二,输出信息。

<span style="font-size:14px;">synchronized(r)
			{
				//判断为true则输出,false则等待
				if(!r.flag)//非真为假,不执行等待
					try{r.wait();}catch(Exception e){}
				System.out.println(r.name+"****"+r.sex);
				r.flag=false;//打印一次将标志置为假,输出线程再次判断非假为真,等待被唤醒。
				r.notify();//唤醒存入线程
			}</span>

注意:对于非静态同步函数来说。它的锁是this,this.wait();  this.notify();

<span style="font-size:14px;">public synchronized void set(String name,String sex)
		{
			if(flag)
				try{this.wait();}catch(Exception e){}

			this.name=name;
			this.sex=sex;

			flag=true;
			this.notify();
		}
</span>

线程之间通讯示例代码:

<span style="font-size:14px;">class Res  //定义一个共同操作的资源,即共享数据
{
	private String name;
	private String sex;
	private boolean flag=false;//初始为假

	public synchronized void set(String name,String sex)
	{
		if(flag)
			try{this.wait();}catch(Exception e){}

		this.name=name;
		this.sex=sex;

		flag=true;
		this.notify();
	}
	public synchronized void out()
	{
		if(!flag)

			try{this.wait();}catch(Exception e){}

		System.out.println(name+"****"+sex);

		flag=false;

		this.notify();
	}
}

class Input implements Runnable   //输入方法
{
	private Res r;
	Input(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		int x=0;
		while(true)
		{
			
				if(x==0)
					r.set("mike","man");
				else
					r.set("丽丽","女女女女女");
				
				x=(x+1)%2;
		}
	}
}

class Output implements Runnable//输出方法
{
	private Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}



class  InputOutputDemo2
{
	public static void main(String[] args) 
	{
		Res r=new Res();  //创建一个对象

		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();
		/*
		Input in=new Input(r);
		Output out=new Output(r);

		Thread t1=new Thread(in);//建立线程
		Thread t2=new Thread(out);

		t1.start();//打开线程
		t2.start();
		*/
	}
}
</span>

四 、线程停止  

如何停止线程?
 * 让run方法结束,即控制循环结束,但是存在特殊情况:当线程处于冻结状态,就不会读到结束标志,线程就不会结束。
 *这时就要对冻结状态进行清除,让线程恢复到运行状态,这样就可以操作标记让线程结束。
 *Thread类中提供了该 方法:interrupt();方法。

代码实例:

<span style="font-size:14px;">class StopThreadDemo {

	public static void main(String[] args) {
		StopThread st = new StopThread();
		
		//创建两个线程并开启。
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		
		t1.start();
		t2.start();
		
		//同时开启一个主线程。
		int num = 0;
		while(true){
			if(num++ ==60){
				//主线程运行结束,此时t1  t2两个线程处于冻结状态,
				//st.changeFlag();
				t1.interrupt();//使用interrupt()方法对冻结状态进行清除。
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"****main****run"+num);
		}
	}

}

class StopThread implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while(flag)
		{
			try {
				wait();//线程t1  t2一进来就等待,即冻结状态。
			} catch (InterruptedException e) {
				//当执行t1 t2冻结状态清除时,发生中断异常,可以在这里将标志置为false,结束这两个线程。
				// TODO: handle exception
				System.out.println(Thread.currentThread().getName()+"****Exception");
				flag = false;
			}
			System.out.println(Thread.currentThread().getName()+"******run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}</span>

五 、多线程中的单例设计模式

懒汉式  多线程访问时出现安全问题  采用同步函数效率低下(加的锁是该类所属的字节码文件对象 类名.class)


代码实例:

<span style="font-size:14px;">class Single
{
	private static Single s=null;
	private Single(){}
	public static Single getInstance()
	{
		if(s==null)//减少判断锁的次数,双重判断
		{
			synchronized(Single.class)//锁是该类所属的字节码文件对象。
			{
				if(s==null)
					s=new Single();
				return s;
			}
		}
	}
}</span>

六 、死锁
造成死锁的原因:同步中嵌套同步,而锁不同。我们在程序设计时要尽量避免死锁这种情况的出现。

示例代码:

<span style="font-size:14px;">package Thread;

public class DeadLockDemo {

	public static void main(String[] args) {
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		
		t1.start();
		t2.start();
	}
}

class Test implements Runnable{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}	
	public void run(){
		if(flag)
		{
			//在持有locka锁的同步代码块中嵌套了持有lockb锁的同步块,
			synchronized (MyLock.locka) {
				System.out.println("if *** locka");
				synchronized (MyLock.lockb) {
					System.out.println("if ***lockb");
				}
			}
		}
		else
		{
			//在持有lockb锁的同步代码块中嵌套了持有locka锁的同步块,
			synchronized (MyLock.lockb) {
				System.out.println("else######lockb");
				synchronized (MyLock.locka) {
					System.out.println("else######locka");
				}
			}
		}
	}
	
}

class MyLock{//存储两个锁。
	static Object locka = new Object();
	static Object lockb = new Object();
}</span>



【Java基础】多线程从入门到掌握 一.多线程基础 1.多任务概念 现代操作系统(Windows,macOS,Linux)都可以执行多任务。多任务就是同时运行多个任务,例如: 同时打开ie浏览器/QQ/QQ音乐 CPU执行代码都是一条一条顺序执行的,即使是单核cpu,也可以同时运行多个任务。 因为操作系统执行多任务实际上就是让CPU对多个任务轮流交替执行。 例如,假设我们有语文、数学、英语3门作业要做,每个作业需要30分钟... 阅读详情

相关推荐

BUSCO评估基因组的完整性

因为数据库收集的时候找的就是在不同的物种间都是单拷贝的基因,所以说在评估的物种里面应该也是单拷贝的状态才对。基因组层面评估:先去基因组上预测这些基因,再去做评估,如果说给他是一个注释好的基因集,这个时候提供的是一个蛋白集合,它就直接是去进行同源搜索,再去评估,看BUSCO数据库里面的这些单拷贝基因有或者无;首先,tblastn也是同源搜索,同源搜索之后,去对转录本进行读码框预测,预测完的结果还是去进行hmmsearch的搜索(hmmsearch是做蛋白质结构域搜索的),最终拿到BUSCO评估结果。

qq_64400864的博客 2959

Java中的多线程你只要看这一篇就够了

引 如果对什么是线程、什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内。 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。 很多人都对...

aikuangchou2298的博客 5199

【ros2】ros2_control 全面解析与实战指南

ros2_control 是 ROS2 生态中用于机器人实时控制的核心框架,旨在统一硬件接入接口、复用控制算法,避免重复开发,实现仿真与实机硬件的无缝迁移。其核心目标是简化不同硬件(传感器、执行器、完整系统)的集成流程,让开发者专注于控制逻辑而非硬件适配。创建类型功能包,依赖rclcpp。实现on_initupdate在中声明控制器插件,编译生成动态库。在 YAML 配置文件中指定自定义控制器类型,加载测试。

EnHengNa的博客 1469

线程——创建方式、常用方法、线程安全问题、通信

一、线程的常用方法 * 1. start():启动当前线程;调用当前线程的run() * 2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 * 3. currentThread():静态方法,返回执行当前代码的线程 * 4. getName():获取当前线程的名字 * 5. setName():设置当前线程的名字 * 6. yield():释放当前cpu的执行权 * 7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态

Guizy 2545

Frida实战:高效dump Android内存中的so文件与修复技巧

本文详细介绍了使用Frida工具dump Android内存中已解密so文件的实战技巧。针对加固应用静态分析失效的痛点,文章提供了从环境搭建、使用frida_dump脚本自动化抓取内存数据,到利用SoFixer工具修复ELF文件结构的完整流程,帮助逆向分析人员高效获取可分析的so文件。

tt34567的博客 527

PostgreSQL高可用之主备复制

多个PostgreSQL可协同工作,如果主库发生宕机备库将迅速进行接管并对外提供服务以实现高可用。本文将从主备复制、主备切换、故障转移等角度进行实际测试演练。第一章主备复制是基础,通过主备复制可以得到一个或多个相同的实例。在主备复制的基础上通过第二章主备切换介绍了如何手工将主库与备库互换角色。在第三章中,采用了Keepalived进行实时监测,在主库宕机后,使用主备切换中提到的命令使备库升级为主库并对外提供服务,以达到PostgreSQL高可用的目标。

asdasdsaff的博客 1347

Camel Splitter分配器以及Aggregate聚合器使用实例

作用:splite分批  aggregate聚合  单线程处理通过splite变成多线程并行处理。 使用场景:一个消息如果它包含多个元素,其中每一个元素可能要以不同的方式去处理,那么我们该如何处理? 分析:需要将不同类型的消息发给别的bundle去做处理,可以根据类型进行分类,然后根据不同类型聚合起来,最后再根据聚合后的对应类型去到不同的bundle做处理。 org.apache.c

必行 2233

【人工智能】AI算法系统设计与算法建模的详细阐述

AI算法系统设计与算法建模是人工智能领域中的核心任务之一,它涉及从问题定义、数据收集、算法选择、模型构建到系统部署的全过程。以下是对AI算法系统设计与算法建模的详细阐述

weixin_51306394的博客 4355

Apache Camel的性能调整思路

时不时地,我会以Camel速度较慢的说法来询问有关优化Camel应用程序的问题。 骆驼只是连接不同系统的粘合剂,路由引擎全都在内存中,并且不需要任何持久状态。 因此,在99%的情况下,性能问题是由于其他系统的瓶颈所致 ,或者是在没有性能考虑的情况下完成了应用程序设计。 如果真是这样,那么进一步调整Camel并没有太大的作用,您必须回到绘图板上。 但是有时候,从您的骆驼路线中挤出几毫秒可...

最佳 Java 编程 2447

解码器选型参数,结构原理,工艺与注意问题总结

解码器芯片(Decoder Chip)是电子设备中的一个重要组件,通常用于处理、解释和输出存储在某种编码格式中的数据或信号。本文对解码器的工作原理,结构特点,工艺流程,选型参数及设计注意事项进行总结。

fydar的博客 1169

OpenClaw实战系列03:Slack/Telegram国际版接入实战指南

在当今的软件开发和工作流自动化中,将项目管理工具与即时通讯平台集成已成为提高团队协作效率的关键。OpenClaw作为一个开源的自动化工具,通过接入Slack和Telegram国际版,可以实现实时通知、自动化任务触发和团队协作的无缝对接。本文将详细介绍如何实现OpenClaw与这两个主流即时通讯平台的集成。OpenClaw是一个开源的自动化工作流引擎,支持多种触发器和执行器,能够帮助开发团队实现CI/CD流程、任务调度、监控告警等自动化场景。模块化设计:支持插件化扩展多触发器支持。

PeterPan的博客 586

Camel框架的快速认识和使用

Camel流程框架是Apache下的一个开源项目,是较为成熟的流程框架。在web项目中也可以无缝地集成于Spring当中。   一、简单使用   引入camel相关的jar包:camel-core-2.10.4.jar。 1、经典的入门示例——文件移动   public class FileMoveWithCamel { public static void main(Str...

Try My Best 尽力而为 2790

网络工程师教程第6版(2024年最新版)

本书 根据《网络工程师考试大纲》(2024年审定通过)编写,对网络工程师岗位所要求的主要知 识及应用技术进行了阐述。本书主要内容包括数据通信、局域网、无线通信网、网络互连、网络安全、网络操作系统与应用服务器、组网技术、网络管理、网络规划和设计。本书是参加网络工程师考试的必备教材,也可作为网络工程从业人员学习网络技术的教材或日常工作的参考用书。王涛和严体华编写了第2章、第3章和第4章;网络工程师教程(第6版)由清华大学出版社出版,由工业和信息化部教育与考试中心组编,张永刚、王涛、高振江任主编,具体介绍如下。

鹿鸣天涯 6901

JAVA多线程基础

JAVA多线程基础 1.什么是并发原理? 多线程:多线程允许我们“同时”执行多段代码。 线程是并发运行的,线程调度会统一规划CPU时间,将CPU的时间划分为若干片段,然后尽可能的均匀分配给所要并发运行的线程,每个线程在获取CPU时间片段后CPU就会运行它的任务,当时间片段用完后,Cpu会离开并执行获取到CPu时间片的线程,所以,所有线程并非真正的“同时”运行着代码,而是走走停停,这种微观上走走停停,在宏观上感觉是同步进行的现象称为“并发运行”。 2.创建线程Thread 1. 方式一:继承Thread并重写

weixin_47600732的博客 4277

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

『Java多线程』基础之基础

Java基础篇~多线程 线程概念 程序(Program) ​ 程序是指令、数据及其组织形式的描述,也就是存储在磁盘或者其他存储设备中含有指令和数据的文件,是一段静态代码 进程(Process) ​ 进程是受操作系统管理的基本单元,是程序的一次动态执行过程,是系统进行资源分配和调度的基本单位,它对应了从代码加载、执行到执行完毕的整个过程,即进程的创建、运行到消亡的过程。从某种程度上来说,进程是正在运行程序的实例,是一个动态概念 多任务(Multi Task) ​ 在一

像写诗一样优雅地写代码~ 7641

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Java之多线程基础

java多线程基础

zhanghuaiwei 444

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Java多线程基础

Java多线程基础

weixin_44525733的博客 1136

java多线程基础

~~

weixin_45780538的博客 680

Java 多线程基础

在Java开发中,我们经常会用到多线程来解决一些异步的问题,多线程属于Java的重点与难点,下面我们就一步一步的讲解Java中的多线程。

HappyHeng的博客 734

Java学习基础笔记——多线程基础部分

Java多线程基础知识

jiangnank的博客 1160

【JAVA】多线程基础

1.多线程基础 ⑴什么是多线程 运行中的exe程序就是一个进程,进程中独立的子任务就是线程 ⑵线程和进程的区别 进程是系统进行资源分配的一个独立单位,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,一个进程可以包括多个线程 ⑶线程的五大状态 新建 创建线程还没有start 就绪 start但是还没有获取CPU时间片 运行 正在执行 阻塞 比如sleep等...

屎蛋的铲屎官 3940

JAVA多线程基础(详解)

实现Runnable接口比继承Thread类所具有的优势:1):适合多个相同的程序代码的线程去处理同一个资源2):可以避免java中的单继承的限制3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类线程同步方法是通过锁来实现,每个对象都有且仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。

欢迎来到空说的博客 1324

java多线程理论基础

多线程是指在一个程序中同时执行多个线程(Thread)。线程是执行程序的最小单元,它可以独立运行,并且可以与其他线程并发执行。多线程的主要目的是实现并发执行,提高程序的效率和资源利用率。在多线程编程中,可以将程序划分为多个线程,每个线程独立执行特定的任务。多个线程可以同时执行不同的任务,从而实现并发处理。每个线程都有自己的执行路径和执行状态,可以独立访问程序的共享资源。多线程编程可以提高程序的响应性能、并发处理能力和资源利用率。

weixin_60843873的博客 271
上一篇: ——反射
下一篇: 网络编程——Java基础
杨鑫校
博客等级 码龄11年 24粉丝 113原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值