Java 的第二十章:多线程

创建线程

继承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.mfbz.cn/a/196639.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在自定义数据集上训练 YOLOv8 实例分割模型

在本文中&#xff0c;我们将介绍微调 YOLOv8-seg 预训练模型的过程&#xff0c;以提高其在特定目标类别上的准确性。Ikomia API简化了计算机视觉工作流的开发过程&#xff0c;允许轻松尝试不同的参数以达到最佳结果。 使用 Ikomia API 入门 通过 Ikomia API&#xff0c;我们只需…

C++ : 初始化列表 类对象作为类成员

传统方式初始化 C 提供了初始化列表语法&#xff0c;用来初始化属性 初始化列表 语法&#xff1a; 构造函数()&#xff1a;属性1(值1), 属性2&#xff08;值2&#xff09;... {} class Person { public://传统方式初始化 Person(int a, int b, int c) {m_A a;m_B b;m_C c…

生成工具集合

文章目录 前言一、SVG 波浪图片生成器二、背景图片生成器三、布局/形状分隔线四、Neumorphism五、动画按钮六、开发工具七、Interactions&#xff08;图片轮播&#xff09;八、CSS Gradient &#xff08;渐变属性&#xff09;九、获取波浪十、平滑阴影十一、CSS Clip-path Make…

图像重定向Image Retarget

1、什么是图像重定向&#xff1f; 图像重定向旨在调整图像的尺寸和比例&#xff0c;以适应不同的显示设备或布局要求。 它可以通过添加或删除像素来改变图像的宽度和高度&#xff0c;同时保持图像的内容和结构的相对比例。 这种技术可以通过保持图像的关键特征和结构来最大程度…

Java-多线程基本知识学习总结

多线程 前言一、线程的创建1、继承Thread类2、实现Runnable接口 二、线程的生命周期三、操作线程的方法1、线程的休眠2、线程的加入3、线程的礼让4、线程的优先级 四、线程同步End 前言 Java是支持多线程的编程语言&#xff0c;所谓多线程就是程序能够同时完成多种操作。 计算…

Linux中执行java命令报错:cannot execute binary file: Exec format error

网上很多文章 都是说操作系统和JDK&#xff0c;32位和64位不兼容问题 当你非常确定你的操作系统是64位&#xff0c;并且JDK也是64位的时候 或者非常确定你的操作系统是32位&#xff0c;并且JDK也是32位的时候 怎么办&#xff1f; 使用以下命令&#xff0c;查看你的操作系统…

IDEA插件推荐

今天给大家推荐一款IDEA插件&#xff1a;Apipost-Helper-2.0&#xff0c;支持三大功能&#xff1a;写完代码IDEA内一键生成API文档&#xff1b;写完代码IDEA内一键调试&#xff1b;生成API目录树&#xff0c;双击即可快速定位API定义的代码…非常好用&#xff01;而且完全免费&…

MySQL进阶-InnoDB引擎

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、语雀 、Github &#x1f389;公众号&#xff1a;猫十二懿 一、InnoDB 逻辑存储引擎 InnoDB的逻辑存储结构如下图所示&#xff1a; 大小关系&#xff1a;表空间&#xff08;Tablespace&#xff0…

消息队列MQ

文章目录 同步通讯和异步通讯什么是MQ?一.RabbitMQ概述二.SpringAMQP1.Work Queue 工作队列2.发布订阅-Fanout Exchange3.发布订阅-DirectExchange4.发布订阅-TopicExchange 3.消息转换器 同步通讯和异步通讯 其实在之前的JAVASE中就已经在线程一章中接触过了同步和异步的概念…

实战:登录接口测试用例举例

以下是一些可能的登录接口测试用例&#xff08;使用Python编写的&#xff09;&#xff1a; 1. 测试正常情况下的登录接口&#xff1a; # 请求URL url "https://example.com/api/login" # 请求参数 username "testuser" password "testpassword&q…

数据治理框架和成熟度模型

数据治理成熟度模型 一个企业的数据治理能力越高&#xff0c;所享受到数据治理带来的价值也会越多&#xff0c;如增加收入、减少成本、降低风险等。于是&#xff0c;很多企业想要准确地评估本公司的数据治理能力&#xff0c;可以利用数据治理成熟度模型方法&#xff0c;包括 D…

摄影网站的技术 SEO:提示和最佳实践

摄影就是要给人留下良好的第一印象。如果你想在竞争中领先&#xff0c;摄影师的SEO是您可以采用的最佳营销方法之一。 我们都曾有过这样的经历&#xff1a;你建立了一个漂亮的作品集网站来吸引更多的业务。网站上线并在社交媒体上推广后&#xff0c;您就可以坐等了。网站访问量…

堆的应用(堆排序、Top-K问题)

文章目录 1 堆排序2 Top-K问题 1 堆排序 堆排序是一种基于二叉堆&#xff08;通常使用数组实现&#xff09;的排序算法。 它的基本思想是利用堆这种数据结构的性质&#xff0c;通过建立一个堆&#xff08;大堆或小堆&#xff09;&#xff0c;使得堆的根节点是所有节点中的最大值…

牛客 算法题 golang语言实现

题目 HJ101 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序 描述 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序数据范围&#xff1a; 1 ≤ &#xfffd; ≤ 10001≤n≤1000 &#xff0c;元素大小满足 0 ≤ &#xfffd; &#…

提升企业网络安全的得力助手——EventLog Analyzer网络日志管理

在当今数字化时代&#xff0c;企业的网络安全问题变得尤为重要。为了更好地应对日益增多的威胁和安全漏洞&#xff0c;企业需要一种高效的网络日志管理工具&#xff0c;EventLog Analyzer便是其中一款卓越的解决方案。 EventLog Analyzer EventLog Analyzer是一款综合性的网络…

探索短剧市场的商机:打造短视频平台的全方位指南

目前短剧市场蓬勃发展&#xff0c;上半年备案数远超电视剧&#xff0c;彰显了短剧小程序市场潜力巨大&#xff0c;商业价值巨大。用户对短小精悍娱乐内容的需求不断增加&#xff0c;而新兴市场中有限的短剧小程序正好能够迎合这一需求。 搭建短视频平台的关键步骤&#xff1a; …

GOAT:多模态、终身学习、平台无关的机器人通用导航系统

机器人应用中涉及到的核心技术包括&#xff1a;环境感知与理解、实时定位与建图、路径规划、行为控制等。GOAT通过多模态结合终生学习的方式让你的机器人可以在未知环境中搜索和导航到任何物体。小白也可以零门槛上手。 项目地址&#xff1a;https://theophilegervet.github.i…

性能优化的一般策略及方法

性能优化的一般策略及方法 在汽车嵌入式开发领域&#xff0c;性能优化始终是一个无法回避的问题&#xff1a; 座舱 HMI 想要实现更流畅的人机交互 通信中间件在给定的 CPU 资源下&#xff0c;追求更高的吞吐量 更一般的场景&#xff1a;嵌入式设备 CPU 资源告急&#xff0c;需…

Web前端开发技术:图像与多媒体文件

在现代的Web开发中&#xff0c;图像和多媒体文件在各种网站和应用程序中扮演着至关重要的角色。它们不仅能提供更丰富的内容&#xff0c;还能大大提高应用程序的吸引力和用户体验。本文将深入介绍一些关键的Web前端开发技术&#xff0c;这些技术将有助于开发者在处理图像和多媒…

【Python3】【力扣题】367. 有效的完全平方数

【力扣题】题目描述&#xff1a; 【Python3】代码&#xff1a; 1、解题思路&#xff1a;Python函数。num的平方根 或者 num的0.5次幂。 知识点&#xff1a;float.is_integer(...)&#xff1a;判断浮点数的值是否等于整数。也可以&#xff1a;浮点数.is_integer()。 pow(a,b)&…
最新文章