Java第二十章多线程

线程简介

在 Java 中,并发机制非常重要。在以往的程序设计中,我们都是一个任务完成后再进行下一个任务,这样下一个任务的开始必须等待前一个任务的结束。Java 语言提供了并发机制,程序员可以在程序中执行多个线程,每一个线程完成一个功能,并与其他线程并发执行,这种机制被称为多线程。然而,有必要强调的是,并不是所有编程语言都支持多线程。
 

创建线程

在 Java 中,主要提供两种方式实现线程,分别为继承 java.lang.Thread 类与实现 java.lang.Runnable接口。

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

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

public class ThreadTest extends Thread{

}

Thread 对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在

run()方法中。run()方法必须使用以下语法格式:

public void run(){
}

主方法线程自动由 Java虚拟机负责,程序员负责启动自己的线程。代码如下:

public static void main(Stringl args) {
new ThreadTest().start();

}

代码:

package 二十;public class ThreadTest extends Thread{public void run() {for(int i = 1;i<=10;i++) {System.out.print(i+" ");}}public static void main(String[] args) {ThreadTest t =new ThreadTest();t.start();}}

运行结果如下:

 

实现Runnable接口

到目前为止,线程都是通过扩展 Thread 类来创建的,如果程序员需要继承其他类(非 Thread 类)而且还要使当前类实现多线程,那么可以通过 Runnable 接口来实现。例如,一个扩展Frame 类的 GUI程序不可能再承 Thread 类,因为 Java 语言中不支持多继承,这时该类需要实现 Runnable 接口使其具有使用线程的功能。实现 Runnable 接口的语法如下:

public class Thread extends Object implements Runnable

Threa类中有以下两个构造方法:

  •  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()方法启动线程。 表明了实现 Runnable 接口创建线程的流程。
代码:

package 二十;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(400,400,500,500);	//绝对定位窗体大小与位置Container container = getContentPane();		//主容器container.setLayout(null);		//使窗体不使用任何布局管理器Icon icon = new ImageIcon("/D:/Java/eclipse/二十/src/西瓜.png");	//图标对象JLabel jL = new JLabel(icon);	//显示图标的标签jL.setBounds(100, 100, 200, 500);	//设置标签的位置与大小Thread t = new Thread() {	//定义匿名线程对象public void run() {while (true) {jL.setBounds(count, 100, 500, 200);	//将标签的横坐标用变量表示try {Thread.sleep(500);	//使线程休眠500毫秒}catch(InterruptedException e) {e.printStackTrace();}count += 9;	//使横坐标每次增加9if(count >=200){count = 10;	//当坐标到达标签的最右边时,使其回到标签最左边}}}};t.start();	//启动线程container.add(jL);	//将标签添加到容器中setVisible(true);	//使窗体可见setDefaultCloseOperation(EXIT_ON_CLOSE);	//设置窗体的关闭方式}public static void main(String[] args) {new SwingAndThread();}}

 运行结果如下:

线程的生命周期

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

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

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

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

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

操作线程的方法

线程的休眠

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

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

package 二十;
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() 方法的加入一个线程时,另外一个线程会等待该线程执行完毕后再继续执行。

代码:

package 二十;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 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();	//启动线程AthreadB = 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);	//当count变量增长为100时break;	//跳出循环}}});threadB.start();	//启动线程B}
}

运行结果如下:

线程的中断

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

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

package 二十;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 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() {@Overridepublic 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 异常。

代码:

package 二十;public class PriorityTest implements Runnable{String name;public PriorityTest(String name) {this.name = name;}@Overridepublic void run() {String tmp = "";for(int i = 0;i < 50000; i++) {	//完成5万次字符串拼接tmp += i;}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;	//设置当前票数@Overridepublic void run() {while(true) {	//设置无限循环if(num >0) {	//设置当前票数是否大于0try {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;	//设置当前票数@Overridepublic void run() {while(true) {	//设置无限循环synchronized(this) {	//同步锁if(num >0) {	//设置当前票数是否大于0try {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(){

}

 代码:

package 二十;public class ThreadSafeTest implements Runnable{int num = 10;	//设置当前票数public synchronized void doit() {	//定义同步方法if(num >0) {	//设置当前票数是否大于0try {Thread.sleep(10);	//使当前线程休眠100毫秒}catch(InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"------票数"+num--);	//票数减一}}@Overridepublic void run() {while(true) {	//设置无限循环doit();	//在run()方法中调用该同步方法}
}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.mzph.cn/news/177857.shtml

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

相关文章

SimpleDateFormat在多线程下的安全问题

目录 情景重现 SimpleDateFormat解析 解决方案 局部变量 加锁 使用线程变量 使用DateTimeFormatter 情景重现 SimpleDateFormat类是Java开发中的一个日期时间的转化类。它可以满足绝大多数的开发场景&#xff0c;但是在高并发下会出现并发问题。接下来查看下文中的案例。…

Python中的datetime库

1. datetime datetime是Python中用于处理日期和时间的类&#xff0c;它包含在datetime模块中。使用datetime类&#xff0c;我们可以创建表示特定日期和时间的对象&#xff0c;以及进行日期和时间的计算和操作。 from datetime import datetime, timedelta# 获取当前日期和时间…

爬虫如何确定HTTP代理IP是否符合自己业务需求?

HTTP代理在许多业务场景中发挥着关键作用&#xff0c;但要确保其能够满足业务需求&#xff0c;需要考虑多个方面的因素。今天我们一起看看&#xff0c;要如何判断HTTP代理是否适合自己的业务&#xff0c;以及在选择HTTP代理时需要考虑的综合因素。 1. 稳定性 稳定性是HTTP代理…

使用Moment.js中获取上周的开始日期和结束日期(可自定义)

前言 有时候需求是这样的&#xff0c;想要获取上周的开始日期和结束日期&#xff0c;或者前几周的时间范围 比如今天是2023.11.28号&#xff0c;我想获取上周的周一到周日&#xff0c;也就是&#xff0c;上周的开始日期: 2023-11-20&#xff0c;上周的结束日期: 2023-11-26 1.…

爬虫系统Docker和Kubernetes部署运维最佳实践

在构建和管理爬虫系统时&#xff0c;使用Docker和Kubernetes可以带来诸多好处&#xff0c;如方便的部署、弹性伸缩和高可靠性。然而&#xff0c;正确的部署和运维实践对于确保系统稳定运行至关重要。在本文中&#xff0c;我将分享爬虫系统在Docker和Kubernetes上的最佳部署和运…

SSM卫生信息管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 卫生信息管理系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模…

java设计模式学习之【单例模式】

文章目录 引言单例模式简介定义与用途实现方式&#xff1a;饿汉式懒汉式 UML 使用场景优势与劣势单例模式在spring中的应用饿汉式实现懒汉式实现数据库连接示例代码地址 引言 单例模式是一种常用的设计模式&#xff0c;用于确保在一个程序中一个类只有一个实例&#xff0c;并且…

centos7搭建 PXE 服务安装 window10/11 系统

最近想搭建之前基于 window server 的 window 批量安装&#xff0c;但想想装 window server 真的太麻烦了&#xff0c;我只是为了 PXE 安装系统而已&#xff0c;这些装一个极度消耗资源的系统真是相当麻烦呀&#xff0c;之前装的 server 不维护的话&#xff0c;不是被挖矿盯上就…

The module to import is incompatible with the current project【鸿蒙开发-BUG已解决】

文章目录 项目场景:问题描述原因分析:解决方案:心得体会:知识点OpenHarmony:HarmonyOS:项目场景: 报错: The module to import is incompatible with the current project 问题描述 希望通过 import module 将该模块引入到我的项目。 导入后出现错误,因为项目和模块…

基于51单片机冰箱温度控制器设计

**单片机设计介绍&#xff0c; 基于51单片机冰箱温度控制器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机冰箱温度控制器设计是一个非常实用的项目。以下是一个基本的介绍&#xff1a; 系统概述&#xff1a; …

Ubuntu20.04用D435i运行VINS-Fusion时出现“追踪与中断点陷阱”的问题该怎么解决

在使用D435i运行VINS-Fusion时出现了以下画面: 该错误显示&#xff1a;追踪与中断点陷阱。在网上查了一堆&#xff0c;说是core file的错误&#xff0c;但修改后也没有解决。实际上&#xff0c;真正的原因是&#xff1a;文件的路径错了。 打开VINS-Fusion的文件所在的空间&…

phpstorm使用教程

1.界面优化 ctrlalts --->Plugins--->搜素Material Theme UI--->安装--->重启phpstorm--->tool--->Material Theme--->Material Theme Chooser--->选择自己喜欢的风格就行 2.安装界面插件之后&#xff0c;修改字体颜色有讲究&#xff1a;需要点击图片…

影刀实例二,小某书如何持续下载图片

一&#xff0c;案例背景&#xff1a; 小某书平台&#xff0c;利用影刀rpa搜索关键词&#xff0c;然后下载对应文章的图片. 二&#xff0c;思路 1. 登录小某书平台&#xff0c;将网页放大最大【手动完成&#xff0c;作为初始状态】 2. 利用影刀命令【打开输入对话框】获得要搜索…

HCIA-H12-811题目解析(2)

1、【单选题】 在以太网这种多点访问网络上PPPOE服务器可以通过一个以太网端口与很多PPPOE客户端建立起PPP连接&#xff0c;因此服务器必须为每个PPP会话建立唯一的会话标识符以区分不同的连接PPPOE会使用什么参数建立会话标识符? 2、【单选题】PPP协议定义的是OSI参考模型中…

【Python】Selenium自动化测试框架

设计思路 本文整理归纳以往的工作中用到的东西&#xff0c;现汇总成基础测试框架提供分享。 框架采用python3 selenium3 PO yaml ddt unittest等技术编写成基础测试框架&#xff0c;能适应日常测试工作需要。 1、使用Page Object模式将页面定位和业务操作分开&#xff0…

ubuntu配置免密登录vscode

1、配置免密登录 &#xff08;1&#xff09;在windows系统cmd下运行命令 ssh-keygen 一路回车&#xff0c;将会在C:\Users\用户名\.ssh目录下生成两个文件&#xff1a;id_rsa和id_rsa.pub。如下图所示。 &#xff08;2&#xff09;进入.ssh目录。如果想使用root用户&#xff0…

抖去推--短视频账号矩阵系统saas工具源码技术开发(源头)

目录 一、短视频矩阵系统搭建常见问题&#xff1f; 二、账号矩阵如何打造&#xff1f;&#xff08;企业号、员工号、达人号裂变&#xff09; 三、无人直播解决什么问题&#xff1f; 一、短视频矩阵系统搭建常见问题&#xff1f; 1、抖去推的短视频AI矩阵营销软件需要一定的技…

Python爬虫404错误:解决方案总结

在进行网络爬虫开发的过程中&#xff0c;经常会遇到HTTP 404错误&#xff0c;即“Not Found”错误。这种错误通常表示所请求的资源不存在。对于爬虫开发者来说&#xff0c;处理这类错误是至关重要的&#xff0c;因为它们可能会导致爬虫无法正常工作。本文将探讨Python爬虫遇到4…

易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC]

文章目录 易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章…

inBuilder低代码平台新特性推荐-第十一期

今天来给大家带来的是inBuilder低代码平台特性推荐系列第十一期——子表启用卡片式编辑&#xff01; 一、 场景介绍 表单子表的列比较多的场景中&#xff0c;若使用列表自带编辑器需要频繁拖动横向滚动条&#xff0c;而且不易进行数据分析、自查。所以项目中希望将子表的数据…