Java面向对象程序开发——多线程开发

news2024/11/17 5:28:50

文章目录

  • 前言
  • 多线程
  • 多线程的实现
    • ①、继承Thread类
      • Thread常用方法
      • Thread的构造器
      • 优缺点
    • ②、实现Runnable接口
      • 优缺点
    • ③、实现Callable接口
    • Runnable接口和继承Thread类的区别
  • 线程同步机制
    • volatile
      • 同步锁
    • 同步方法
    • lock锁
  • 线程池


前言

线程(thread)是一个程序内部的一条执行路径。
我们之前启动程序执行后,main方法的执行其实就是一条单独的执行路径。

程序中如果只有一条执行路径,那么这个程序就是单线程的程序。

多线程是指从软硬件上实现多条执行流程的技术
或者说多线程是指多个可以同时并发运行的程序

消息通信、淘宝、京东系统等都离不开多线程技术。


在这里插入图片描述

多线程

并发:在一个时间段出现的,可能不是同时发生
并行:同时发生
进程:一个任务创建、运行、消亡
线程:进程的一个单元,一个进程可以有多个线程。

多线程的实现

多线程实现方式有3种:
继承Thread类
实现Runnable接口重写run方法(方法无返回值 )
实现Callable接口,重写call方法(方法可以有返回值 )

①、继承Thread类

① 定义一个子类,重写run方法
② 实例化子类
③ 对子类对象执行start方法来启动线程
(直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。只有调用start方法才是启动一个新的线程执行。)

public class Test {
	public static void main(String[] args)  {
		MyThread myThread = new MyThread();//2.实例化子类,创建Thread的子类对象
		myThread.start();  //3.对子类对象执行start方法来启动线程
		for(int i =0; i<10; i++) {
			System.out.println("主线程"+i);
		}
	 }	
}

class MyThread extends Thread{ //1.自定义一个子类,重写run方法
	@Override
	public void run(){
		for(int i =0; i<10; i++) {
			System.out.println("子线程"+getName()+i); //getName()返回线程名称
		}
	}
}

Java是通过java.lang.Thread 类来代表线程的。
按照面向对象的思想,Thread类应该提供了实现多线程的方式。

Thread常用方法

String getName​()  //获取当前线程的名称,默认线程名称是Thread-索引
void setName​(String name)  //将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称
public static Thread currentThread():  //返回对当前正在执行的线程对象的引用
public static void sleep(long time)  //让当前线程休眠指定的时间后再继续执行,单位为毫秒(Thread类的线程休眠方法)
public void run()  //线程任务方法
public void start()  //线程启动方法

注意:
1、此方法是Thread类的静态方法,可以直接使用Thread类调用。
2、这个方法是在哪个线程执行中调用的,就会得到哪个线程对象。

Thread的构造器

public Thread(String name)  //可以为当前线程指定名称
public Thread(Runnable target)  //封装Runnable对象交给线程对象
public Thread(Runnable target,String name)  //封装Runnable对象成为线程对象,并指定线程名称

优缺点

优点:编码简单
缺点:存在单继承的局限性,线程类继承Thread后,不能继承其他类,不便于扩展。不能返回线程执行结果

②、实现Runnable接口

① 声明一个Runnable接口实现类,重写run方法
② 实例化,把实例化对象作为线程的目标进行创建
③ 执行start方法来启动线程

public class Test {
	public static void main(String[] args)  {
		MyThread myThread = new MyThread();//2.实例化
		Thread t = new Thread(myThread);//2.把实例化对象myThread作为Thread的target进行创建
		t.start();//启动线程
		for(int i =0; i<10; i++) {
			System.out.println("主线程"+i);
		}//主线程和子线程在抢夺资源,所以每次运行结果都不一样
	 }	
}

//1.声明一个Runnable接口实现类,重写run方法
class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i =0; i<10; i++) {
			System.out.println("子线程"+Thread.currentThread().getName()+i); //getName()返回线程名称
		}
	}
}

优缺点

优点:线程任务类只是实现了Runnale接口,可以继续继承类和实现接口,扩展性强
缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的

③、实现Callable接口

与Thread类、Runnable接口对比它是可以有返回值

public class Demo3 implements Callable<String>{

	//实现call方法
	@Override
	public String call() throws Exception {
		System.out.println("Demo3开始运行");
		Thread.sleep(3000);  //暂停3000毫秒
		System.out.println("Demo3运行结束");
		return "二哈喇子";
	}
}

测试类:

public class 实现Callable接口线程测试 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		Demo3 d3 = new Demo3();
		//创建FutureTask类对象 获得线程执行后返回值
		FutureTask<String> ft = new FutureTask<>(d3);
		//创建Thread类对象
		Thread thread = new Thread(ft);
		
		thread.start();
		String result = ft.get();
		
		System.out.println("Demo3执行完的结果是"+result);
		System.out.println("main执行结束");
	}
}

Runnable接口和继承Thread类的区别

这个问题面试的时候经常问

① 、创建线程的方式不同

② 、设置线程名方式不同

③、获取线程名方式不同

④ 、由于Java是单继承,一个类继承Thread类以后不能继承其他类,扩展性不好。而实现Runnable接口则可以侧面实现了多继承

⑤、继承Thread类不能实现线程变量资源共享,注意,是线程里的变量实现Runnable 接口线程变量是可以共享的,也可以不共享,看创建线程的方式

线程同步机制

模拟电影院卖100张票

public class Test {
	public static void main(String[] args)  {
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread);//线程1
		Thread t2 = new Thread(myThread);//线程2
		t1.start();
		t2.start();
	 }	
}
class MyThread implements Runnable{
	private int num =100;//定义一个共享票源
	@Override
	public void run() {
		while(num > 0) {//还有票
			try {
				Thread.sleep(50);//为了模拟买票操作耗时
				System.out.println(Thread.currentThread().getName()+"正在卖"+num+"票");//获取当前线程名
				 num--;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			 
		}
	}
}

线程安全问题是由全局变量及静态变量引起的,若多线程同时执行写操作(单纯读操作一般是线程安全的),线程会去抢夺cpu资源完成操作,造成线程不安全。

同步机制:
1、volatile
2、同步锁
3、同步方法
4、CAS
5、Lock锁

synchronized(同步锁){
	需要同步操作的代码:1锁对象可以是任意类型。2多个线程对象要使用同一把锁。
}

public synchronized void method(){
	可能会产生线程安全问题的代码
}

class Ticket implements  Runnable{
    Lock lock = new ReentrantLock();
    private int num = 100;//定一个多线程共享的票源
    @Override
    public void run() {
        while (true){
         //上锁
            lock.lock();
            if (num>0){
              ...//代码省略
            }
            //释放锁
            lock.unlock();    
        }
    }
}

volatile

假如说线程1修改了data变量的值为1,然后将这个修改写入自己的本地工作内存。
那么此时,线程1的工作内存里的data值为1。
然而,主内存里的data值还是为0!线程2的工作内存里的data值还是0啊?!

作用:

1、一旦data变量定义的时候前面加了volatile来修饰的话,那么线程1只要修改data变量的值,就会在修改完自己本地工作内存的data变量值之后,强制将这个data变量最新的值刷回主内存,必须让主内存里的data变量值立马变成最新的值!
2、如果此时别的线程的工作内存中有这个data变量的本地缓存,也就是一个变量副本的话,那么会强制让其他线程的工作内存中的data变量缓存直接失效过期,不允许再次读取和使用了!
3、如果线程2在代码运行过程中再次需要读取data变量的值,他就必须重新从主内存中加载data变量最新的值!那么不就可以读取到data = 1这个最新的值了!

同步锁

在这里插入图片描述

public class Test {
	public static void main(String[] args)  {
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread);//线程1
		Thread t2 = new Thread(myThread);//线程2
		t1.start();
		t2.start();
	 }	
}
class MyThread implements Runnable{
	private int num =100;//定义一个共享票源
	Object obj = new Object();
	@Override
	public void run() {
		while(true) {//窗口永远开启
			synchronized(obj) {//同步锁
				if(num > 0) {
					try {
						Thread.sleep(50);//为了模拟买票操作耗时
						System.out.println(Thread.currentThread().getName()+"正在卖"+num+"票");//获取当前线程名
						 num--;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}				 
		}
	}
}

同步方法

public class Test {
	public static void main(String[] args)  {
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread);//线程1
		Thread t2 = new Thread(myThread);//线程2
		t1.start();
		t2.start();
	 }	
}
class MyThread implements Runnable{
	private int num =100;//定义一个共享票源
	Object obj = new Object();
	@Override
	public void run() {
		while(true) {//窗口永远开启
			method();	 
		}
	}
	public synchronized void method(){ //同步方法
		if(num > 0) {
			try {
				Thread.sleep(50);//为了模拟买票操作耗时
				System.out.println(Thread.currentThread().getName()+"正在卖"+num+"票");//获取当前线程名
				 num--;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

CAS(Compare and Set)

lock锁

使用ReentrantLock实现同步, lock()方法上锁,unlock()方法释放锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
  public static void main(String[] args)  {
    MyThread myThread = new MyThread();
    Thread t1 = new Thread(myThread);//线程1
    Thread t2 = new Thread(myThread);//线程2
    t1.start();
    t2.start();
   }  
}
class MyThread implements Runnable{
  private int num =100;//定义一个共享票源
  Lock lock = new ReentrantLock();  //声明一个lock对象
  @Override
  public void run() {
    while(true) {//还有票
    	lock.lock();   //上锁
    	if(num > 0) {
    		try {
    	        Thread.sleep(50);//为了模拟买票操作耗时
    	        System.out.println(Thread.currentThread().getName()+"正在卖"+num+"票");//获取当前线程名
    	         num--;
    	      } catch (InterruptedException e) {
    	        e.printStackTrace();
    	      }
    	}
    	lock.unlock(); //释放锁
    }
  }
}

线程池

问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

好处

1、降低资源消耗,如果线程池无空闲,则需等待其他任务执行完毕后归还线程。

2、提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

3、提高线程的可管理性,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*1.创建线程池对象
2.实现Runnable接口子类
3.使用线程池
4.关闭线程池*/
public class Test {
  public static void main(String[] args)  {
    //1.创建线程池对象
	  ExecutorService service = Executors.newFixedThreadPool(2);  //包含2个线程对象
	  MyThread m = new MyThread();  //2.实现Runnable接口子类
//	  Thread t = new Thread(m);
//	  t.start();
	  service.submit(m);//3.使用线程池
	  service.submit(m);//submit执行之后程序并没有关闭,是因为线程池控制了线程的关闭
	  service.submit(m);
	//  service.shutdown();//4.关闭线程池
	 // service.submit(m); //关闭之后再执行则报异常
   }  
}
class MyThread implements Runnable{
	@Override
	public void run() {
		 System.out.println("售票员开始卖票");
		 try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}//为了模拟买票操作耗时
		 System.out.println(Thread.currentThread().getName()+"正在卖票");//获取当前线程名
		 System.out.println("线程结束,回到线程池");
	}
}

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

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

相关文章

shared_ptr产生内存泄漏的场景

使用 shared_ptr 可以帮助管理动态分配的内存&#xff0c;它使用引用计数的方式来跟踪共享对象的引用数量&#xff0c;当引用计数为零时&#xff0c;会自动释放内存。然而&#xff0c;shared_ptr 也存在一些潜在的内存泄漏的场景&#xff0c;下面是一些常见的情况&#xff1a; …

LVS和keepa lived群集

keepa lived 简述 一.keepalived 服务重要功能 1管理LS负载均衡器软件 keepalived可以通过读取自身的配置文件&#xff0c;实现通过更底层的接口直接管理Lvs配置以及服务的启动 停止功能 这会使 LVS应用跟更加简便 2 支持故障自动切换 (failover) ①两台知己同时安装好kee…

网络字节序和套接字

4.1主机字节序列和网络字节序列 主机字节序列分为&#xff1a;大端字节序和小端字节序 大端&#xff1a;高位字节存储在内存的低地址处&#xff0c;低位字节存储在内存的高地址处。 小端&#xff1a;高位字节存储在内存的高地址处&#xff0c;低位字节存储在内存的低地址处。…

【算法基础】数据结构

链表 单链表 826. 单链表 - AcWing题库 #include<bits/stdc.h> using namespace std; const int N 100010; int m; int e[N],ne[N];//记录数据和下一结点坐标 int head,idx;//当前指向的结点 void init() {head-1;idx0; } void addtohead(int x) {e[idx]x;ne[idx]hea…

前端框架Vue

Vue 介绍 官方网站&#xff1a;https://cn.vuejs.org/ Vue.js是一种用于构建用户界面的开源JavaScript框架。它是一种轻量级的框架&#xff0c;易于学习和使用。它基于标准HTML&#xff0c;CSS和JavaScript构建&#xff0c;并提供了一套生声明式的&#xff0c;组件时的编程模…

【深入浅出 Spring Security(十三)】使用 JWT 进行前后端分离认证(附源码)

使用 JWT 进行前后端分离认证 一、JWT 的简单介绍二、使用 JWT 进行安全认证后端结合SpringSecurity实现前端Vue3结合Pinia、Axios实现测试结果 一、JWT 的简单介绍 JWT 全称 Java web Token&#xff0c;在此所讲述的是 JWT 用于身份认证&#xff0c;用服务器端生成的JWT去替代…

spring--Ioc控制反转/DI依赖注入

IOC控制反转-解耦 1.概念&#xff1a;在使用对象的时候&#xff0c;由主动的new转换为外部提供对象&#xff0c;将对象创建的控制权交给外部&#xff0c;即控制反转 2.spring提供了一个容器&#xff0c;称为IOC容器&#xff0c;用来从当ioc中的外部 3.被管理或者被创建的对象在…

ChatGPT实战:如何规划自己的职业生涯?

ChatGPT的出现&#xff0c;不仅改变了人们对人工智能技术的认识&#xff0c;也对经济社会发展产生了深远的影响。那么&#xff0c;在ChatGPT时代&#xff0c;人们应该如何规划自己的职业呢&#xff1f; 职业规划是一个有意义且重要的过程&#xff0c;它可以帮助你在职业生涯中…

什么是BI可视化?企业管理决策为什么要用BI系统?

在当今的商业环境中&#xff0c;数据已经成为企业决策制定的重要基础。然而&#xff0c;面对海量的数据&#xff0c;如何有效地分析和利用这些数据&#xff0c;成为了企业管理者面临的一大挑战。BI(Business Intelligence)系统应运而生&#xff0c;它可以帮助企业管理者从复杂的…

基于Java企业人事管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

如何用好强大的 TDengine 集群 ? 先了解 RAFT 在 3.0 中的应用

大家都知道&#xff1a;由于单机数据库在数据规模、并发访问量等方面存在瓶颈&#xff0c;无法满足大规模应用的需求。因此才有了把数据切割分片&#xff0c;分布存储分布处理在多个节点上的数据库&#xff0c;也就是分布式数据库的由来。 而为了实现数据库的高可用&#xff0…

新装Ubuntu虚拟机环境--基本配置流程

新装Ubuntu虚拟机环境--基本配置流程 安装vmware tools修改源 安装vmware tools 点击菜单栏–>虚拟机–>安装VMwaretools桌面上有个图标&#xff0c;点进去 打开这个压缩包–>Extract到桌面&#xff08;or别的文件夹都可以&#xff0c;如果提示空间不足就换其它文件夹…

手把手写一个LED驱动(1)

目录 1.开启驱动开发之路 1.1、驱动开发的准备工作 1.2、驱动开发的步骤 1.3、实践 2.最简单的模块源码分析 2.1、常用的模块操作命令 2.2、模块的安装 2.3、模块的版本信息vermagic 2.4、模块卸载 2.5、模块中常用宏(MODULE_xxx这种宏的作用是用来添加模块描述信息) …

2023年仪器仪表行业研究报告

第一章 行业概况 仪器仪表行业是指专门从事研究、设计、制造、销售和服务于科学研究、生产和生活中所需的各种仪器、仪表和自动化设备的行业。这些仪器和仪表可以用于测量、指示、记录、调节和控制物理、化学和生物过程中的各种参数。这个行业涵盖了广泛的设备和系统&#xff…

今日分享:Midjourney巧妙地用参考图/垫图来绘画图

大家都知道AI绘画工具每次生成的效果都是随机的&#xff0c;但是现在很多AI绘图工具都提供了利用参考图/垫图的方式出图&#xff0c;这样就可以让让AI画作生成自己想要的布局、场景、色彩等等。 国内的AI绘图工具一般都好操作&#xff0c;国外主流的Midjourney也可以添加参考图…

利用for循环和innerHTML在div中再填入多个div

目录 棋盘给每个小格子加上不同的id加上不同的参数传入 我们设置小格子的点击触发事件 循环填充元素 棋盘 先做棋盘&#xff0c;点击出现 <!DOCTYPE HTML> <html><head><meta charset"utf-8"><style>.sty1{background-color:#aaa;he…

旅游网站制作搭建,为旅行业务带来新机遇

旅游业在全球范围内一直都是蓬勃发展的行业之一。随着互联网的普及以及人们对旅行需求的增加&#xff0c;拥有一个精美而功能强大的旅游网站已经成为了旅行从业者的必备条件。本文旨在简单介绍旅游网站是什么&#xff0c;旅游网站的好处&#xff0c;并提供一些快速制作搭建旅游…

10亿上下文!微软新作,引入LongNet将Transformers上下文长度扩充到10亿

夕小瑶科技说 原创 作者 | python, ZenMogre Transformer处理长序列时较为吃力。因为global attention的存在&#xff0c;模型的时间复杂度是序列长度的2次方级。为了建模更长的上下文&#xff0c;人们也提出了各种稀疏注意力机制。而这次&#xff0c;微软卷到家了&#xff0…

word免费转为pdf怎么转,分享这几个方法给大家!

将Word文档转换为PDF格式是一种常见的需求&#xff0c;因为PDF文件具有广泛的兼容性和安全性。本文将介绍三种免费转换Word为PDF的方法&#xff0c;包括记灵在线工具、使用Word自带功能以及使用Smallpdf。这些方法简单易行&#xff0c;帮助您轻松完成转换&#xff0c;方便与他人…

【尚医通】vue3+ts前端项目开发笔记——项目分析

尚医通开发笔记 一、项目分析 项目在线地址&#xff1a;http://syt.atguigu.cn测试帐号&#xff1a;17720125002 首页 home header 全局组件布局 左&#xff1a;logo 、title中&#xff1a;初始隐藏 搜索框 公共组件显示条件&#xff1a;在页面滚动到页面内搜索框的位置显示…