第20章 多线程

news2024/11/25 0:30:42

创建线程

继承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 接口启动新的线程的步骤:

  • 建立 Runnable 对象
  • 使用参数为 Runnable 对象的构造方法创建 Thread 实例
  • 调用 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,300,400,400);//绝对定位窗体大小与位置
		Container cotainer=getContentPane();//主容器
		cotainer.setLayout(null);//使窗体不使用任何布局管理器
		
		Icon icon=new ImageIcon("src/线程/3.gif");//图标对象
		JLabel jl=new JLabel(icon);//显示图标的标签
		jl.setBounds(30, 40, 200, 40);//设置标签的位置与大小
		Thread t=new Thread() {//定义匿名线程对象
			public void run() {			
			while(true) {
				jl.setBounds(count,100, 500, 250);//将标签的横坐标用变量表示
				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 提供了线程同步的机制来防止资源访问的冲突。

线程安全

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

package 线程;

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){
}

package 线程;

public class ThreadSafeTest implements Runnable{
	int num=10;//设置当前总票数
	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();
	}

}

package 线程;

public class ThreadSafeTest implements Runnable{
	int num=10;//设置当前总票数
	public synchronized  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();
	}

}

 结果:

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

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

相关文章

第一百八十二回 自定义一个可以滑动的刻度尺

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法3. 示例代码4. 内容总结我们在上一章回中介绍了"如何绘制阴影效果"相关的内容,本章回中将介绍 如何自定义一个可以滑动的刻度尺.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 任何优美的文字在图…

[SaaS] 广告创意中stable-diffusion的应用

深度对谈&#xff1a;广告创意领域中 AIGC 的应用这个领域非常快速发展&#xff0c;所以你应该保持好奇心&#xff0c;不断尝试新事物&#xff0c;不断挑战自己。https://mp.weixin.qq.com/s/ux9iEABNois3y4wwyaDzAQ我对AIGC领域应用调研&#xff0c;除了MaaS服务之外&#xff…

电荷泵升压/降压电路

一、升压\降压电路原理分析 1、升压电路 电荷泵升压电路 VoutVa5V 5V_PLUS0V时&#xff0c;Va给C2充电&#xff0c;C2上节点电压比C2下节点电压高Va&#xff1b; 5V_PLUS5V时&#xff0c;C2电压不能突变&#xff0c;C2上节点电压依然比C2下节点电压高Va&#xff0c;但C2下节点…

supermap-iserver激活教程(linux)

本篇只介绍linux临时许可激活教程&#xff0c;windows的原理一摸一样不做赘述。 1.下载许可中心&#xff08;web版&#xff09; SuperMap技术资源中心|为您提供全面的在线技术服务 2.解压 supermap-bslicense-server-3.0.24-linux-x64.tar.gz tar -zxvf supermap-bslicense…

选择排序以及改进方案

选择排序以及改进方案 介绍&#xff1a; 选择排序是一种简单直观的排序算法&#xff0c;它的基本思想是在未排序序列中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;然后将其放在已排序序列的末尾。选择排序的过程就像是每次从待排序的元素中选择最小的一个&…

Java基于ssm的租房求租网站房东直租中介托管房屋出租项目源码

演示视频 https://www.bilibili.com/video/BV1Lh411J7ic/?share_sourcecopy_web&vd_source11344bb73ef9b33550b8202d07ae139b 可转成springboot项目。 主要功能&#xff1a;租户可以浏览搜索收藏房源&#xff0c;预约看房&#xff0c;发布求租信息。房东可以发布管理房源…

2023-11-28-直播单细胞图表美化-seurat数据结构 featureplot dotplot vlnplot

单细胞常见的可视化方式有DimPlot&#xff0c;FeaturePlot &#xff0c;DotPlot &#xff0c;VlnPlot 和 DoHeatmap几种 &#xff0c;Seurat中均可以很简单的实现&#xff0c;但是文献中的图大多会精美很多。 之前 跟SCI学umap图| ggplot2 绘制umap图&#xff0c;坐标位置 &am…

SSL证书实惠品牌——JoySSL

随着互联网的普及和发展&#xff0c;网络安全问题日益严重。为了保护网站数据的安全&#xff0c;越来越多的网站开始使用SSL证书。JoySSL证书作为一款高性价比的SSL证书&#xff0c;受到了广泛的关注和好评。 目前市面上主流的证书基本上都是国外证书&#xff0c;也就是说你在验…

【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏4(附项目源码)

文章目录 本节最终效果前言命中敌人闪白和击退效果敌人死亡效果等级 击杀数 经验绘制经验条显示等级和杀敌数游戏倒计时玩家血条参考源码完结 本节最终效果 前言 本节紧跟着上一篇&#xff0c;主要实现敌人受击死亡效果特效&#xff0c;主角等级、击杀数、经验、血条UI显示。 …

C# datagridview控件 绑定数据库中表中数据的方式-3

1.如下图所示&#xff0c;为数据库中的一张表结构&#xff0c;注意该表中共有11个字段 2.首先在窗体后台代码中拖入一个datagridview控件&#xff0c;并在窗体加载时&#xff0c;给datagridview控件添加列&#xff0c;添加的方式如下所示&#xff1a;请注意&#xff0c;每个列…

机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割

一、简述 虽然Segment Anything用于图像分割的通用大模型看起来很酷(飞桨也提供分割一切的模型),但是个人感觉落地应用的时候心里还是更倾向于飞桨这种场景式的,因为需要用到一些人体分割的需求,所以这里主要是对飞桨高性能图像分割开发套件进行了解和使用,但是暂时不训练…

docker镜像原理

什么是镜像 容器解决应用开发、测试和部署的问题&#xff0c;而镜像解决应用部署环境问题。镜像是一个只读的容器模板&#xff0c; 打包了应用程序和应用程序所依赖的文件系统以及启动容器的配置文件&#xff0c;是启动容器的基础。镜像所打 包的文件内容就是容器的系统运行环…

SLURM资源调度管理系统REST API服务配置,基于slurm22.05.9,centos9stream默认版本

前面给大家将了一下slurm集群的简单配置&#xff0c;这里给大家再提升一下&#xff0c;配置slurm服务的restful的api&#xff0c;这样大家可以将slurm服务通过api整合到桌面或者网页端&#xff0c;通过桌面或者网页界面进行管理。 1、SLURM集群配置 这里请大家参考&#xff1…

【古月居《ros入门21讲》学习笔记】14_参数的使用与编程方法

目录 说明&#xff1a; 1. 参数模型&#xff08;全局字典&#xff09; 2. 实现过程&#xff08;C&#xff09; 创建功能包 参数命令行的使用 YAML参数文件 rosparam命令 使用示例 编程方法&#xff08;C&#xff09; 配置代码编译规则 编译并运行 编译 运行 3. 实…

开源堡垒机Jumpserver

文章目录 开源堡垒机JumpserverJumpserver介绍安装环境部署安装jumpserver访问jumpserver的web界面 开源堡垒机Jumpserver Jumpserver介绍 Jumpserver 是全球首款完全开源的堡垒机&#xff0c;使用 GNU GPL v2.0 开源协议&#xff0c;是符合 4A 的运维安全审计系统。 Jumpse…

天鹅湖国家旅游度假区 | 展柜OLED透明屏:创新展示提升互动体验

天鹅湖国家旅游度假区 | 展柜OLED透明屏 产品&#xff1a;一块55寸OLED透明屏嵌入玻璃安装 应用场景&#xff1a;用在天鹅湖国家旅游度假区——三门峡城市文化客厅展馆中的一个透明展示柜&#xff0c;用一块55寸OLED透明屏嵌入展示柜的玻璃&#xff0c;让观众即可以看到展柜里…

【赠书第9期】巧用ChatGPT高效搞定Excel数据分析

文章目录 前言 1 操作步骤 1.1 数据清理和整理 1.2 公式和函数的优化 1.3 图表和可视化 1.4 数据透视表的使用 1.5 条件格式化和筛选 1.6 数据分析技巧 1.7 自动化和宏的创建 2 推荐图书 3 粉丝福利 前言 ChatGPT 是一个强大的工具&#xff0c;可以为你提供在 Exce…

【axios封装】万字长文,TypeScript实战,封装一个axios - 基础封装篇

目录 前言版本环境变量配置引入的类型1、AxiosIntance: axios实例类型2、InternalAxiosRequestConfig: 高版本下AxiosRequestConfig的拓展类型3、AxiosRequestConfig: 请求体配置参数类型4、AxiosError: 错误对象类型5、AxiosResponse: 完整原始响应体类型 目标效果开始封装骨架…

算法通关第十三关-青铜挑战数学基础问题

数组元素积的符号 描述 : 已知函数 signFunc(x) 将会根据 x 的正负返回特定值&#xff1a; 如果 x 是正数&#xff0c;返回 1 。如果 x 是负数&#xff0c;返回 -1 。如果 x 是等于 0 &#xff0c;返回 0 。 给你一个整数数组 nums 。令 product 为数组 nums 中所有元素值的…

视觉测量基础

1. 相机模型 1.1 坐标系转换原理 世界坐标系(world Coords):点在真实世界中的位置&#xff0c;描述相机位置。 相机坐标系(Cameras Coords):以相机光学系统中心&#xff08;镜头中心&#xff09;为原点&#xff0c;建立相机坐标系。 图像物理坐标系(Film Coords):经过小孔成…