第20 章 多线程

news2024/11/25 11:56:08

20.1线程简介.


20.2创建线程
2.1继承Thread类
Thread 类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立Thread 实例。Thread类中常用的两个构造方法如下:

public Thread():创建一个新的线程对象。
public Thread(String threadName):创建一个名称为threadName的线程对象。
继承Thread 类创建一个新的线程的语法如下:

public class ThreadTest extends Thread{
}

完成线程真正功能的代码放在类的run(方法中,当一个类继承Thread类后,就可以在该类中覆盖 run()方法,将实现该线程功能的代码写入run()方法中,然后调用Thread类中的start()方法执行线程,也就是调用run()方法。
Thread 对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在 run()方法中。run()方法必须使用以下语法格式:

public void run(){
}

注意如果 start()方法调用一个已经启动的线程,系统将抛出IllegalThreadStateException异常。

2.2实现Runnable接口(单继承,所以要使用接口)
实现接口语法:

public class Thread extends  Object implements Runnable

实现Runnable接口的程序会创建一个Thread 对象,并将Runnable对象与Thread对象相关联。Thread    
类中有以下两个构造方法:


☑public Thread(Runnable target)
☑public Thread(Runnable target,String name)

这两个构造方法的参数中都存在Runnable实例,使用以上构造方法就可以将Runnable 实例与 Thread 实例相关联。
使用Runnable接口启动新的线程的步骤如下:

(1)建立Runnable 对象。
(2)使用参数为Runnable对象的构造方法创建Thread实例。(3)调用start()方法启动线程。
通过 Runnable接口创建线程时,程序员首先需要编写一个实现 Runnable接口的类,然后实例化该类的对象,这样就建立了Runnable对象;接下来使用相应的构造方法创建 Thread 实例;最后使用该实例调用Thread类中的 start()方法启动线程。

 import java.awt.Container;
 
import javax.swing.JFrame;
import javax.swing.JLabel;
 
public class SwingAndThread extends JFrame{
	int count =0;
	public SwingAndThread(){
		setBounds(300,200,250,100);	//绝对定位窗体大小与位置	
		Container container = getContentPane();	//主容器	
		container.setLayout(null);	//使窗体不使用任何布局管理器	
		lcon icon = new lmagelcon("/D20/src/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 >= 200){
		count = 10;	//当图标到达标签的最右边时,使其回到标签最左边	
		}
		}
		}
		};	t.start();	container.add(jl);	//启动线程	//将标签添加到容器中	
		setVisible(true);	//使窗体可见	
		setDefaultCloseOperation(EXIT_ON_CLOSE);	//设置窗体的关闭方式
	}
 
	public static void main(String[] args) {
		new SwingAndThread();
 
	}
 
}

20.3线程的生命周期
线程具有生命周期,其中包含7种状态,分别为出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态。出生状态就是线程被创建时处于的状态,在用户使用该线程实例调用 start()方法之前线程都处于出生状态;当用户调用start()方法后,线程处于就绪状态(又被称为可执行状态);当线程得到系统资源后就进入运行状态。
一旦线程进入可执行状态,它会在就绪与运行状态下转换,同时也有可能进入等待、休眠、阻塞或死亡状态。当处于运行状态下的线程调用Thread类中的wait()方法时,该线程便进入等待状态,进入等待状态的线程必须调用Thread类中的notify0方法才能被唤醒,而调用notifyAl1O)方法可将所有处于等待状态下的线程唤醒;当线程调用Thread类中的sleep(方法时,则会进入休眠状态。如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时线程进入就绪状态,对于阻塞的线程来说,即使系统资源空闲,线程依然不能回到运行状态。当线程的run()方法执行完毕时,线程进入死亡状态。
说明
使线程处于不同状态下的方法会在20.4节中进行讲解,在此读者只需了解线程的多个状态即可

20.4操作线程的方法
4.1线程的休眠
  

	import java.awt.*;
	import java.util.Random; import javax.swing.*;
	public class SleepMethodTest extends JFrame {
	private static Color[]color={ Color.BLACK, Color.BLUE,Color.CYAN,Color.GREEN,Color.ORANGE, Color.YELLOW, Color.RED,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;	Il定义初始坐标	
		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,100,y++);	//绘制直线并递增垂直坐标	
		if (y >=80) {
		y = 50;
		}
		}
		}
		});
		t.start();	//启动线程	
		}
	
		public static void main(String[]args){
		init(new SleepMethodTest(),100,100);
		}
		public static void init(JFrame frame, int width, int height){//初始化程序界面的方法
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
		frame.setSize(width, height); frame.setVisible(true);
		
	}
 
}

4.2线程的加入
使用join()方法加入线程

import java.awt.BorderLayout;
import javax.swing.*;
public class JoinTest extends JFrame {
private Thread threadA;	//定义两个线程	
private Thread threadB;
private JProgressBar progressBar = new JProgressBar();	//定义两个进度条组件	
private JProgressBar progressBar2=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(progressBar, BorderLayout.NORTH); //将进度条设置在窗体最北面 getContentPane().add(progressBar2,BorderLayout.SOUTH); //将进度条设置在窗体最南面
		progressBar.setStringPainted(true);	//设置进度条显示数字字符	
		progressBar2.setStringPainted(true);
		threadA = new Thread(new Runnable() {	//使用匿名内部类形式初始化 Thread实例	
	int count = 0;
	public void run() {	//重写 run()方法	
			while (true) {
				progressBar.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) {
		progressBar2.setValue(++count);	//设置进度条的当前值	
		try{	Thread.sleep(100);	//使线程B休眠 100 毫秒	
		} catch (InterruptedException e) {
		e.printStackTrace();
		if (count ==100)	break;	//跳出循环	
		}
		}
		});	//启动线程B	
		threadB.start();
		}
	}

 4.3线程的中断
现在提倡在r un()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。
如果线程是因为使用了sleep()或 wait()方法:进入了就绪状态,可以使用Thread 类中interrupt)方法使线程离开run()方法,同时结束线程,但程序会会抛出 InterruptedException异常,用户可以在处理该异常时完成线程的中断业务处理,如终止while 循环。
下面的实例演示了某个线程使用interrupted0方法,同时程序抛出了InterruptedException异常,在异常处理时结束了while循环。在项目中,经常在这里执行关闭数据库连接和关闭Socket连接等操作。

import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
	public class InterruptedSwing extends JFrame {
		
		public InterruptedSwing(){
		JProgressBar progressBar = new JProgressBar();	//创建进度条	
		getContentPane().add(progressBar, BorderLayout.NORTH);//将进度条放置在窗体合适位置
		JButton button=new JButton("停止");
		getContentPane().add(button,BorderLayout.SOUTH);
		progressBar.setStringPainted(true);	//设置进度条上显示数字	
		Thread t = new Thread(new Runnable(){
		int count = 0; public void run() {
		while (true){
			progressBar.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);
		
	}
 
}

 4.4线程的礼让
Thread类中提供了一种礼让方法,使用 yield()方法表示,它只是给当前正处于运行状态的线程一
个提醒,告知它可以将资源礼让给其他线程    但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
yield()方法使具有同样优先级的线程有过进入可执行状态的机会,在当前线程放弃执行权时会再度回到就绪状态。对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配 CPU 时间片来执行。

20.5线程的优先级
 

public class PT implements Runnable {
	String name;
	public PT(String name) {
	this.name = name; 
	}
	@Override
	public void run() {
	String tmp= "";
	for (int i= 0; i< 50000; i++) {
	tmp +=i;
//完成5万次字符串拼接
	}
	System.out.println(name +"线程完成任务");
	}
	public static void main(String[]args){
	Thread a = new Thread(new PT("A"));
	a.setPriority(1);	//A线程优先级最小	
	Thread b = new Thread(new PT("B"));
	b.setPriority(3);
	Thread c = new Thread(new PT("C"));	
	c.setPriority(7);	
	Thread d = new Thread(new PT("D"));
	d.setPriority(10);	//D线程优先级最大	
	a.start();	
	b.start();	
	c.start();	
	d.start();	
	}
	}

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

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


6.1 线程安全

实际开发中,使用多线程程序的情况很多,在编写多线程程序时,应该考虑到线程安全问题,实质上线程安全问题来源于两个线程同时存取单一对象的数据。

 

 
	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--);//票数减1
		}
	}
	}
		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();
		}
}

从这个结果可以看出,最后打印出的剩下的票数为负值,这样就出现了问题。这是由于同时创建了4个线程,这4个线程执行run()方法,在num变量为1时,线程一、线程二、线程三、线程四都对num 变量有存储功能,当线程一执行run()方法时,还没有来得及做递减操作,就指定它调用sleep()方法进入就绪状态,统等。这种多线程的    这时线程二、线程三和线程四也都进入了run()方法,发现num 变 如果大于0则执行    量依然大于 0,但此时线程一休眠时间已到,将num变量值递减张票),第一个线程    同时线程二、线程三、线程四也都对num变量进行递减操作,从 而产生了负值。

6.2线程同步机制
1.同步块
Java 中提供了同步机制,可以有效地防止资源冲突。同步机制使用synchronized关键字, 
 语法如下: 

synchronized (Object){
}

通常将共享资源的操作放置在synchronized定义的区域内,这样当其他线程获取到这个锁时,就必须等待锁被释放后才可以进入该区域。Object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时首先检查该对象的标志位,如果为0状态,表明此同步块内存在其他线程,这时当期线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码后,这时该对象的标识位设置为1,当期线程才能开始执行同步块中的代码,并将Object对象的标识位设置为0,以防止其他线程执行同步块中的代码。

public class SynchronizedTest 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();}
						//票数减 1
						System.out.println(Thread.currentThread().getName()+"--票数" + num--);
						}
				}
			}
		}
						public static void main(String[] args){
						//实例化类对象
						SynchronizedTest t = new SynchronizedTest();//以该类对象分别实例化4个线程 
						Thread tA= new Thread(t,"线程一"); 
						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,否则就会出错。
修改例20.7的代码,将共享资源操作放置在一个同步方法中,代码如下:

int num = 10;
public synchronized void doit() {    //定义同步方法
if(num>0){
try{
Thread.sleep(10);
}catch(interruptedException e){
e.printStackTrace():
System.out.printin(Thread.currentThread().getName()+"-票数” +num--);
public void run(){
	while(true){
doit():	//在run()方法中调用该同步方法


 

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

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

相关文章

BTCPay Server:免费、安全、开源的比特币支付处理器 | 开源日报 No.90

MunGell/awesome-for-beginners Stars: 58.0k License: NOASSERTION 这个项目是一个收集开源项目的列表&#xff0c;旨在帮助初学者找到可以贡献代码的机会。该列表按编程语言分类&#xff0c;并列出了每个项目以及其标签 (如 “good-first-issue”、“beginner” 等)。主要功…

宇宙中可能存在的另外一种生命体-硅基生命

本文提到的所有图片有燧原曜图平台生成提供 概念 碳基&#xff1a;生命的组织架构由碳元素组成&#xff08;地球的生命&#xff09;硅基&#xff1a;生命的组织架构由硅元素组成 引言 费米悖论 中提到有存在外星文明的可能&#xff0c;但它们迄今为止还无法和我们接触三体人…

【Java】文件路径-绝对路径与相对路径

1、绝对路径与相对路径 先来看一下绝对路径和相对路径的定义&#xff1a; 绝对路径是指完整的描述文件位置的路径就是绝对路径。如Windows系统中的D:\Project\data\test.txt&#xff0c;MAC系统中的/Users/liuwenwen/Desktop/Project/test.txt 相对路径是指相对于当前文件位置…

nodejs最新电商jd m端h5st 4.2签名算法4.2版本逆向,jd API接口,jd商品数据采集

前言&#xff1a; jd m端使用最新的h5st 4.2签名算法&#xff0c;与h5st 4.1版本有很大的不同。在这儿分析一下&#xff0c;供大家参考。 一、目标地址(Base64解码) aHR0cHM6Ly9zby5tLmpkLmNvbS93YXJlL3NlYXJjaC5hY3Rpb24/a2V5d29yZD0lRTklOTklQTQlRTYlQjklQkYlRTYlOUMlQkEmc2…

云性能监控的关键组成部分

云性能监控是确保云服务稳定运行的不可或缺的一环。通过实时可见性、资源优化和快速故障排除&#xff0c;企业可以最大程度地利用云服务的优势&#xff0c;提高业务的可用性和效率。采用综合的监控策略和最佳实践&#xff0c;企业可以更好地应对日益复杂和变化的云环境&#xf…

最强Oracle 19c OCM考试错题解析

参加过Oracle 19c OCM考试的学员都知道&#xff0c;OCM考试结束后会告诉考生考试成绩是多少分&#xff0c;但并不会告诉考生哪道题做错了&#xff0c;就考试问题&#xff0c;姚远老师会总结一些文章出来&#xff0c;欢迎关注。 最近姚远老师在OCM的授课中发现了一道题目&#x…

1-2、计算机组成

语雀原文链接 文章目录 1、计算机组成1-1、主板1-2、接口卡1-3、存储器&#xff08;内存&#xff09; 2、存储器2-1、存储器的重要性2-2、物理存储器2-3、逻辑存储器2-4、内存地址空间的分段2-5、存储单元2-6、指令和数据 3、总线3-1、三类总线3-2、CPU读数据例子3-3、CPU写数…

SVG图片选择库组件封装及使用

需求 需求&#xff1a; 在项目中通常需要做菜单管理&#xff0c;想要让左侧菜单好看一点&#xff0c;一般都会选择添加图标&#xff0c;需要自定义选择喜欢的图标&#xff0c;得提供一个有选择项的图标库 延伸需求&#xff1a;在项目中通常可能有好几个图标选择库&#xff0c;可…

【星火大模型】api使用

讯飞星火官方首页 准备工作 注册讯飞星火账号申请开发者api试用 从一个demo开始 讯飞星火官方的程序员为我们提供了非常优秀的demo&#xff0c;基本涵盖了大多数常用语言。 demo下载链接 这里我选用Java带上下文调用示例 下载后可以看到这是一个idea项目&#xff0c;直接…

最佳实践-使用RKE快速部署高可用k8s集群

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 概述 Rancher Kubernetes Engine&#xff08;RKE&#xff09;是一个用于部署、管理和运行Kubernetes集群的开源工具。旨在简化Kubernetes集群的部署和操作。 RKE具有以下特点和功能&…

Linux学习笔记09、Shell命令之历史命令和自动补全

上一篇&#xff1a;Linux学习笔记08、Shell命令之常用命令缩写及全称 目录 1、历史命令&#xff1a; 1.1、查看所有历史命令列表&#xff1a; 1.2、查看指定历史命令&#xff1a; 1.3、清除历史命令&#xff1a; 2、自动补全 2.1、当字符串唯一时&#xff1a; 2.2、当有多个…

一个00后女生在csdn创作1095的创作纪念日

大家好&#xff0c;我是你们熟悉的微枫奶糖 肥晨&#xff0c;今天收到私信&#xff0c;突然发现&#xff0c;我来到CSDN已经1095天了。让我来分享一下吧~ 机缘 不知不觉来csdn已经1095天了&#xff0c;祝贺我在CSDN的创作纪念日&#xff01; 这是一个非常值得纪念的日子&…

初识PO模式并在Selenium中简单实践

初识PO模式 PO&#xff08;PageObject&#xff09;是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中&#xff0c;通过类与类之间的调用完成特定操作。 PO被认为是自动化测试项目开发实践的最佳设计模式之一。 在学习PO模式前&#xff0c;可以先…

HeteroTiC: 基于异构时间通道的鲁棒网络流水印

文章信息 论文题目&#xff1a;HeteroTiC: A robust network flow watermarking based on heterogeneous time channels 期刊&#xff08;会议&#xff09;&#xff1a;Computer Networks 时间&#xff1a;2022 级别&#xff1a;CCF B 文章链接&#xff1a;https://doi.org/10…

堆在排序中的应用

堆排序 1、堆排序原理 堆排序是利用到了堆这种数据结构&#xff0c;我们首先回顾一下二叉堆的特性&#xff1a; 最大堆的堆顶是整个堆中的最大元素。最小堆的堆顶是整个堆中的最小元素。 以最大堆为例&#xff0c;如果删除一个最大堆的堆顶&#xff08;并不是完全删除&…

YOLOv5改进之ShuffleNetV2

目录 一、原理 网络结构 二、代码 三、应用到YOLOv5 一、原理

关于神经网络,你不得不知的三大要点

什么是神经网络&#xff1f; 神经网络是一个具有相连节点层的计算模型&#xff0c;其分层结构与大脑中的神经元网络结构相似。神经网络可通过数据进行学习&#xff0c;因此&#xff0c;可训练其识别模式、对数据分类和预测未来事件。 神经网络将您的输入细分为多个抽象层。比…

班级管理经验分享

班级管理经验如同作战策略&#xff0c;需要不断摸索和实践。以下是我结合自身经历&#xff0c;总结出的一些班级管理经验&#xff0c;与大家分享。 一、了解学生 作为班主任&#xff0c;首先要了解每个学生的性格、爱好、学习情况、家庭背景等。这需要我们与学生建立良好的沟通…

C++——取地址和自增运算

今天做了道题一下给我整蒙了&#xff0c;把实验探索发篇文章&#xff0c;题目如下&#xff1a; int a[]{10,11,12},*p&a[0];执行完*p;*p1;后a[0],a[1],a[2]的值各为多少 答案是 10, 12, 12怎么理解*p,*p1呢&#xff0c;我觉得可以用汉语把代码读一下&#xff0c;p&#xf…

Make sure that using this pseudorandom number generator is safe here.

问题类型&#xff1a;安全热点 安全问题级别&#xff1a;MEDIUM 一、问题代码 工具类Package&#xff1a; Java commons-lang3 库 RandomUtils 随机数工具类 import org.apache.commons.lang3.RandomUtils; 用法&#xff1a; RandomUtils.nextInt(0, 999999999) //生成 0…