Java 的第二十章:多线程

news2024/11/27 12:02:34

创建线程

继承Thread 类

        Thread 类时 java.lang 包中的一个类,从类中实例化的对象代表线程,程序员启动一个新线程需要建立 Thread 实例。

         Thread 对象需要一个任务来执行,任务是指线程在启动时执行的工作,start() 方法启动线程,该工作的功能被写在run() 方法中。

例:让线程循环打印1~10的数字 

public class ThreadTest extends Thread{
	public void run() {
		for(int i=0;i<=10;i++) {
			System.out.print(i+" ");
		}
	}

	public static void main(String[] args) {
		ThreadTest t=new ThreadTest();
			t.start();
		}
}

运行结果:

实现 Runnable 接口

        线程都是通过扩展 Thread 类来创建的,如果程序员需要继承其他类(非Thread 类),而且还要是当前类实现多线程,那么可以通过  Runnable 接口来实现。

        实现 Runnable 接口的程序会创建一个 Thread 对象,并将 Runnable 对象与 Thread 对象相关联。

使用 Runnable 接口启动新的线程的步骤:

  1. 建立 Runnable 对象
  2. 使用参数为 Runnable 对象的构造方法创建 Thread 实例
  3. 调用 start() 方法启动线程

例:让窗体的图标动起来

import java.awt.Container;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class SwingAndThread extends JFrame{
	int count=0;//图标坐标

	public  SwingAndThread(){
		setBounds(300,200,250,100);//绝对定位窗体大小与位置
		Container cotainer=getContentPane();//主容器
		cotainer.setLayout(null);//使窗体不使用任何布局管理器
		
		Icon icon=new ImageIcon("1.gif");//图标对象
		JLabel jl=new JLabel(icon);//显示图标的标签
		jl.setBounds(10,10,200,50);//设置标签的位置与大小
		Thread t=new Thread() {//定义匿名线程对象
			public void run() {			
			while(true) {
				jl.setBounds(count,10,200,50);//将标签的横坐标用变量表示
				try {
					Thread.sleep(500);//使线程休眠500毫秒
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				count+=4;//使横坐标每次增加4
				if(count>=120) {
					count=10;//当图标到达标签的最右时,时其回到标签做左边
				}
			}
			
		}
	};
	t.start();//启动线程
	cotainer.add(jl);//将标签添加到容器中
	setVisible(true);//使窗体可见
	setDefaultCloseOperation(EXIT_ON_CLOSE);//设置窗体的关闭方式
	}

	public static void main(String[] args) {
		new SwingAndThread();

	}
}

运行结果:

线程的生命周期

        一旦线程进入可执行状态,它会在就绪与运行状态下转换,同时也有可能进入等待,休眠,赌塞或死亡状态。

要使线程处于就绪,有以下几种方法:

  • 调用 sleep() 方法。
  • 调用 wait() 方法。
  • 等待输入/输出完成。

当线程处于就绪状态后,可以用以下几种方法使线程再次进入运行状态:

  • 线程调用 notify() 方法。
  • 线程调用 notifyAll() 方法。
  • 线程调用 interrupt() 方法。
  • 线程的休眠时间结束。
  • 输入/输出结束。

操作线程的方法

 线程的休眠

        一种能控制线程行为的方法是调用 sleep() 方法需要一个参数用于指定该线程休眠的时间,该时间以毫秒为单位。

例:每0.1秒绘制一条随机颜色的线条

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

import javax.swing.JFrame;

public class SleepMethodTest extends JFrame{
	private static Color[] color= {Color.BLACK,Color.BLUE,Color.CYAN,Color.GREEN,
		Color.RED,Color.ORANGE,Color.YELLOW,Color.PINK,Color.LIGHT_GRAY};//定义颜色数组
	private static final Random rand=new Random();//创建随机对象
	
	private static Color getC() {//获取随机颜色值的方法
		return color[rand.nextInt(color.length)];
	}
	
	public  SleepMethodTest(){
		Thread t=new Thread(new Runnable() {//创建匿名线程对象
			int x=30;//定义初始坐标
			int y=50;
			
			public void run() {
				while(true) {//无限循环
					try {
						Thread.sleep(100);//线程休眠0.1秒
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					Graphics graphics=getGraphics();//获取组件绘图上下文对象
					graphics.setColor(getC());//设置绘图颜色
					graphics.drawLine(x, y,150, y++);//绘制直线并递增垂直坐标
					if(y>=180) {
						y=50;
					}
				}
			}
		});
		t.start();//启动线程
	}

	public static void main(String[] args) {
		init(new SleepMethodTest(),200,200);

	}

	public static void init(JFrame frame,int width,int height) {//初始化程序界面的方法
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(width, height);
		frame.setVisible(true);
	}
}

运行结果:

 

线程的加入

        当某个线程使用 join() 方法的加入一个线程时,另外一个线程会等待该线程执行完毕后再继续执行。

例:让进度条A等待进度条B

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JProgressBar;

public class JoinTest extends JFrame{
	//定义两个线程
	private Thread threadA;
	private Thread threadB;
	//定义两个进度条组件
	private JProgressBar porgressBar=new JProgressBar();
	private JProgressBar porgressBar2=new JProgressBar();
	

	public static void main(String[] args) {
		JoinTest test=new JoinTest();
		test.setVisible(true);

	}
	public  JoinTest() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(200,200,200,100);
		getContentPane().add(porgressBar,BorderLayout.NORTH);//将进度条设置在窗体最北面
		getContentPane().add(porgressBar2,BorderLayout.SOUTH);//将进度条设置在窗体最南面
		//设置进度条显示数字字符
		porgressBar.setStringPainted(true);
		porgressBar2.setStringPainted(true);
		//使用匿名内部类形式初始化 Thread 实例
		threadA=new Thread(new Runnable() {
			int count=0;
			public void run() {//重写 run()方法
				while(true) {
					porgressBar.setValue(++count);//设置进度条的当前值
					try {
						Thread.sleep(100);//使线程A休眠100毫秒
						threadB.join();//使线程B调用join()方法
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		threadA.start();//启动线程A
		threadB=new Thread(new Runnable() {
			int count=0;
			public void run() {
				while(true) {
					porgressBar2.setValue(++count);//设置进度条的当前值
					try {
						Thread.sleep(100);//使线程休眠100毫秒
					}catch(InterruptedException e) {
						e.printStackTrace();
				}
					if(count==100)//当count变量增长为100时
						break;//跳出循环
				}
			}
		});
		threadB.start();//启动线程B
	}

}

运行结果:

线程的中断

        以往有时候会使用 stop() 方法停止线程,但当前版本的 JDK 早已废除了 stop() 方法,不建议使用 stop() 方法来停止一个线程的运行。现在提倡在 run() 方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。

        如果线程是因为使用了 sleep()或 wait()方法进入了就入就绪状态,可以使用 Thread()方法,同时程序破除了 InterruptedException 异常,在异常处理时结束了 while 循环。在项目中,经常在这里执行关闭数据连接和关闭 Socket 连接等操作。

例:单机按钮停止进度条滚动

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;

public class InterruptedSwing extends JFrame{
	
	public InterruptedSwing (){
		JProgressBar porgressBar=new JProgressBar();//创建进度条
		getContentPane().add(porgressBar,BorderLayout.NORTH);//将进度条设置在窗体最北面
		JButton button=new JButton("停止");
		getContentPane().add(button,BorderLayout.SOUTH);//将进度条设置在窗体最北面
		//设置进度条显示数字字符
		porgressBar.setStringPainted(true);
		//使用匿名内部类形式初始化 Thread 实例
		Thread t=new Thread(new Runnable() {
			int count=0;
			
			public void run() {//重写 run()方法
				while(true) {
					porgressBar.setValue(++count);//设置进度条的当前值
					try {
						Thread.sleep(100);//使线程休眠100毫秒
					}catch(InterruptedException e) {//捕捉InterruptedException异常
						System.out.println("但前线程被中断");
						break;
					}
				}
			}
		});
		
		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				t.interrupt();//中断线程
				
			}
			
		});
		t.start();//启动线程
	}
	
	public static void init(JFrame frame,int width,int height) {//初始化程序界面的方法
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(width, height);
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		init(new InterruptedSwing(),100,100);

	}
}

运行结果:

线程的礼让

        Thread 类提供了一种礼让方法,使用 yied()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。

        yied()方法使具有同样优先级的线程有进入可执行状态的机会,在当前线程放弃执行权时再度回到就绪状态。对于支持多任务的操作系统来说,不需要调用 yied()方法,因为操作系统会为线程自动分配 CPU 时间来执行。

线程的优先级

        每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这并不意味着低优先级的线程得不到运行,而只是它运行的概率比较小,如垃圾回收线程的优先级就按照较低。

        线程的优先级可以使用 setPriority()方法调整,如果使用该方法设置的优先级不在 1~10,将产生IllegalArgumentException 异常。

例:观察不同优先级的线程执行完毕顺序


public class PriorityTest implements Runnable{
	String name;
	
	public PriorityTest(String name) {
		this.name=name;
	}
	
	@Override
	public void run() {
		String tmp="";
		for(int i=0;i<5000;i++) {
			tmp+=i;//完成5万次字符串拼接
		}
		System.out.println(name+"线程完成任务");
	}

	public static void main(String[] args) {
		Thread a=new Thread(new PriorityTest("A"));
		a.setPriority(1);//A线程优先级最小
		Thread b=new Thread(new PriorityTest("B"));
		b.setPriority(3);
		Thread c=new Thread(new PriorityTest("C"));
		c.setPriority(7);
		Thread d=new Thread(new PriorityTest("D"));
		d.setPriority(10);//D线程优先级最大
		a.start();
		b.start();
		c.start();
		d.start();
	}
}

运行结果:

线程同步

        在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,但是如果使用多线程程序,就会发生两个线程抢占资源的问题,如两个人同事说话、两个人同时过同一个独木桥。所以,在多线程编程中需要防止这些资源访问的冲突。Java 提供了线程同步的机制来防止资源访问的冲突。

线程的安全

        在编写多线程时时,因该考虑到线程安全问题。实质上线程问题来源两个线程同时存取单一对象的数据。

例:实现 Runnable 接口,在未考虑到线程安全问题的基础上,模拟火车站售票系统的功能


public class ThreadSafeTest implements Runnable{
	int num=10;//设置当前总票数


	public  void run() {
		while(true) { //设置无限循环
			if(num>0) {//判断当前票数是否大于0
				try {
					Thread.sleep(100);//使当前线程休眠100毫秒
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"---票数"+num--);//票数减一
			}
		}
	}
	
	public static void main(String[] args) {
		ThreadSafeTest t=new ThreadSafeTest();//实例化类对象
		Thread tA=new Thread(t,"线程一");//以该类对象分别实例化4个线程
		Thread tB=new Thread(t,"线程二");
		Thread tC=new Thread(t,"线程三");
		Thread tD=new Thread(t,"线程四");
		tA.start();//分别启动线程
		tB.start();
		tC.start();
		tD.start();
	
	}

}

运行结果:

线程同步机制

        所以解决多线程资源问题的方法基本上都是采用给定时间只允许一个线程访问共享资源的方法。这时就需要给共享源上一道锁。这就好比一个人上洗手间时,他进入洗手间后会将门上锁,出来是再将锁打开,然后其他人才可以进入。

1、同步块

        Java中提供了同步机制,可以有效地防止资源冲突。同步机制使用 synchronized 关键字,使用该关键字包含的代码块称为同步块,也称临界区,语法如下:

synchronized(Object){

}

例:开发线程安全的火车售票系统  


public class ThreadSafeTest implements Runnable{
	int num=10;//设置当前总票数


	@Override
	public void run() {
		while(true) {//设置无限循环
			synchronized(this) {//设置同步代码块
			if(num>0) {//判断当前拍哦书是否大于0
				try {
					Thread.sleep(100);//使当前线程休眠100毫秒
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"---票数"+num--);//票数减一
			}
		}
		}
	}
	

	public static void main(String[] args) {
		ThreadSafeTest t=new ThreadSafeTest();//实例化类对象
		Thread tA=new Thread(t,"线程一");//以该类对象分别实例化4个线程
		Thread tB=new Thread(t,"线程二");
		Thread tC=new Thread(t,"线程三");
		Thread tD=new Thread(t,"线程四");
		tA.start();//分别启动线程
		tB.start();
		tC.start();
		tD.start();
	
	}

}

运行结果:

2、同步方法

        同步方法就是在方法前面用 synchronized 关键字修饰的方法,语法如下:

synchronized void f(){ }

        当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为 synchronized,否则就会报错。

例:修改开发线程安全的火车售票系统的代码,将共享资源操作放置在一个同步方法中


public class ThreadSafeTest implements Runnable{
	int num=10;//设置当前总票数


	public  synchronized void doit() {
		
			if(num>0) {//判断当前拍哦书是否大于0
				try {
					Thread.sleep(100);//使当前线程休眠100毫秒
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"---票数"+num--);//票数减一
			}
		}
		
	public void run() {
		while(true) { //设置无限循环
			doit();
		}
	}
	

	public static void main(String[] args) {
		ThreadSafeTest t=new ThreadSafeTest();//实例化类对象
		Thread tA=new Thread(t,"线程一");//以该类对象分别实例化4个线程
		Thread tB=new Thread(t,"线程二");
		Thread tC=new Thread(t,"线程三");
		Thread tD=new Thread(t,"线程四");
		tA.start();//分别启动线程
		tB.start();
		tC.start();
		tD.start();
	
	}

}

运行结果:

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

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

相关文章

Docker 使用心得

创建一个docker 镜像&#xff0c;相关运行代码&#xff0c;放在docker镜像文件同级&#xff0c; pm2 不能与 docker一起使用&#xff08;&#xff09; # node 服务docker FROM node:10.16.3LABEL author"sj"RUN mkdir -p /var/nodeCOPY ./node /var/nodeWORKDIR /va…

Vue实现图片预览(Viewer.js)

摘要&#xff1a; vue项目开发中遇到一个图片预览的需求&#xff0c;可以切换下一张&#xff0c;就是花里胡哨的&#xff0c;所以找viewer.js的插件 npm install v-viewer -S在项目main.js中加入&#xff1a; Viewer.setDefaults用于更改默认配置&#xff0c;比如我不想要显示…

基于AT89C51单片机的倒数计时器设计

1&#xff0e;设计任务 利用AT89C51单片机为核心控制元件,设计一个简易的数字电压表&#xff0c;设计的系统实用性强、操作简单&#xff0c;实现了智能化、数字化。 本设计采用单片机为主控芯片&#xff0c;结合周边电路组成LED彩灯的闪烁控制系统器&#xff0c;用来控制红色…

Paraformer 语音识别原理

Paraformer(Parallel Transformer)非自回归端到端语音系统需要解决两个问题&#xff1a; 准确预测输出序列长度&#xff0c;送入预测语音信号判断包含多少文字。 如何从encoder 的输出中提取隐层表征&#xff0c;作为decoder的输入。 采用一个预测器&#xff08;Predictor&…

【Node.js】笔记整理 5 - Express框架

写在最前&#xff1a;跟着视频学习只是为了在新手期快速入门。想要学习全面、进阶的知识&#xff0c;需要格外注重实战和官方技术文档&#xff0c;文档建议作为手册使用 系列文章 【Node.js】笔记整理 1 - 基础知识【Node.js】笔记整理 2 - 常用模块【Node.js】笔记整理 3 - n…

IDEA maven无法下载源代码处理

1、使用idea内置maven 在idea中新增一个mvn运行项,截图如下: 输入命令: dependency:resolve -Dclassifiersources 2、如果外部maven&#xff0c;不使用idea内部maven 在工程目录下命令行执行命令: mvn dependency:resolve -Dclassifiersources

HX3002入耳检测光感驱动调试-感0x08 寄存器溢出,不变化错误问题解决方法

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 读取光感0x08 寄存器溢出,不变化错误问题?原因 原因:没有读取到0x08数据,没有读0x…

2的幂运算

2的幂 描述 : 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 题目 : LeetCode 231.2的幂 : 231. 2 的幂 分…

vue3实现element table缓存滚动条

背景 对于后台管理系统&#xff0c;数据的展示形式大多都是通过表格&#xff0c;常常会出现的一种场景&#xff0c;从表格跳到二级页面&#xff0c;再返回上一页时&#xff0c;需要缓存当前的页码和滚动条的位置&#xff0c;以为使用keep-alive就能实现这两种诉求&#xff0c;…

centos服务器安装docker和Rabbitmq

centos服务器 一 centos安装docker1 安装docker所需要的依赖包2配置yum源3查看仓库中所有的docker版本4安装docker5 设置docker为开机自启6验证docker是否安装成功 二 使用docker安装RabbitMQ拉取RabbitMQ镜像创建并运行容器 一 centos安装docker 1 安装docker所需要的依赖包 …

RocketMQ-快速实战

MQ简介 MQ&#xff1a;MessageQueue&#xff0c;消息队列。是在互联网中使用非常广泛的一系列服务中间件。 Message&#xff1a;消息。消息是在不同进程之间传递的数据。这些进程可以部署在同一台机器上&#xff0c;也可以分布在不同机器上。&#xff08;数据形式&#xff1a…

NASM安装和结合nodepad++进行编译的过程

mov ax,0x30 mov bx,0xc0 add ax,bx times 502 db 0 db 0x55 db 0xAA nasm安装地址: https://www.nasm.us/ 下载exe安装 在命令行提示符输入nasm编译命令 nasm exam.asm -f bin -o exam.bin 此时输入回车将会执行编译过程。 1&#xff0c;启动NotePad&#xff0c;在菜单上选…

【驱动】串口驱动分析(三)-serial driver

简介 前两节我们介绍串口驱动的框架和tty core部分。这节我们介绍和硬件紧密相关的串口驱动部分。 UART驱动部分依赖于硬件平台&#xff0c;而TTY驱动和具体的平台无关。虽然UART部分依赖于平台&#xff0c;但是不管是哪个硬件平台&#xff0c;驱动的思路都是一致的&#xff…

vue3中的provide与inject跨层级组件(祖孙)间通信

provide和inject提供依赖注入&#xff0c;功能类似 vue2.x 的provide/inject 实现跨层级组件(祖孙)间通信 子或孙子组件接收到的数据可以用于读取显示&#xff0c;也可以进行修改&#xff0c;同步修改父&#xff08;祖&#xff09;组件的数据。 注意&#xff1a;无论子组件…

微服务--08--Seata XA模式 AT模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 分布式事务Seata 1.XA模式1.1.两阶段提交1.2.Seata的XA模型1.3.优缺点 AT模式2.1.Seata的AT模型2.2.流程梳理2.3.AT与XA的区别 分布式事务 > 事务–01—CAP理论…

Constraintlayout

goneMargin 约束的View隐藏时的margin 约束链风格 chainStyle 权重 bias 设置宽高比 w,h 百分比 GuideLine 基线 上下的间距 Group 指定一系列View进行绑定进行操作 通过init加载 然后setIds进行绑定 然后通过group进行操作 Layer 设置动画 Barrier Flow

QT线程的使用 循环中程序的等待

QT线程的使用 循环中程序的等待 先看效果1 pro文件2 头文件3 源文件4 ui文件先看效果 1 pro文件 QT += concurrent2 头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H

⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)

1.这里我代码没啥问题~~~编辑器里也没毛病 void Start(){// 加载底图和上层图片string backgroundImagePath Application.streamingAssetsPath "/background.jpg";Texture2D backgroundTexture new Texture2D(2, 2);byte[] backgroundImageData System.IO.File.R…

qt5.15播放音频示例(4种方法)

文章目录 Qt播放音频方法一 QMediaPlayer方法二 QSound方法三 QSoundEffect方法四 QAudioOutput问题1 播放无声问题2 QAudioOutput播放嗡嗡声的问题参考Qt播放音频 在linux系统中,可以通过aplay进行简单的播放音频,如 aplay /opt/Audio/test.wav在图形界面,也可以封装apla…