Java-线程安全的四个经典案例和线程池

news2024/11/19 16:43:33

单例模式

有些对象,在一个程序中应该只有唯一 一个实例(光靠人保证不靠谱 借助语法来保证) 就可以使用单例模式

在单例模式下 对象的实例化被限制了 只能创建一个 多了的也创建不了

单例模式分为两种:饿汉模式和懒汉模式

饿汉模式:

饿急眼了,不吃(创建)不行了,就是在类定义时就创建一个实例

Class Singleton{
private static singleton instance= new singleton();
public static Singleton getInstance(){
return instance;
}
Private Singleton(){
}(防止new实例)
};

static 静态 实际效果和字面含义没啥关系 实际的含义是 类属性/类方法(对应实例属性实例方法)类对象只能创建一个

效果是无论get多少个实例都是同一个实例 且无法new实例

懒汉模式:

ε=(´ο`*))) 啥时候用啥时候再创建吧

class Singletonlazy {
         private static Singgletonlazy instance = null;
         public static Singletonlazy getInstance(){
         If(instance == null){
            Instance = new Singletonlazy();
}
Return instance;
}
}

 那么回到多线程,饿汉模式和懒汉模式是线程安全的吗

饿汉模式get只是多线程读 没问题

懒汉模式 第一次调用get有的地方在读有的地方在写 实例创建好之后就安全了(一个线程在读 一个线程在创建 还没创建完 就读了 就又创建了)

所以我们可以选择对它加锁

synchronized(SingletonLazy.class){
 If(instance == null){
            Instance = new Singletonlazy();
}}

但是如果这么写的话,每次的判断操作也被锁了,多线程中还是不可以同时运行(一个判断另一个就会阻塞)所以我们可以

if(instance==null){
synchronized(SingletonLazy.class){
 If(instance == null){
            Instance = new Singletonlazy();
}}}

在单线程中我们连续使用两个相同的if没有意义 但是隔了一个synchronized就是另一说了 

饿汉模式和懒汉模式是一种设计思想 不是固定的代码 我们再举一个例子

1.饿汉 把10g都读取到内存中 读取完毕之后再允许用户进行查看和修改

2.懒汉 一次只读一点 随着用户翻页 继续再读后续内容

生产者消费者模型

先来了解一下 阻塞队列

阻塞队列和正常队列的区别是啥子呢 就是当对列为空 尝试出队列 就会阻塞。队列为满时 尝试入队列也会阻塞

生产者消费者模型就是通过阻塞队列实现的

我们用包饺子来举例 把包出饺子作为最终目标(没有煮饺子 吃饺子的环节)

一个人擀饺子皮 其他人包(因为擀面杖数量有限 所以效率会远高于 每个人自己擀饺子皮自己包)

生产者消费者模型(必须得有盖帘才算生产者消费者模型)

生产者:负责擀饺子皮

盖帘: 阻塞队列

消费者:负责包饺子的人

生产者消费者模型的好处是什么

1.可以做到更好的解耦合(高内聚就是写一个功能的时候尽量让这个功能的代码集中放置 不要东一块西一块)

如果生产者和消费者直接交互 耦合性很高 b寄了可能给a也干碎了

如果 生产者->阻塞队列->消费者 双方只想着和阻塞队列交互就可以了 他俩谁寄了都不影响对方

2.削峰填谷(就跟三峡大坝似得 把雨季的水给旱季) 提高整个系统的抗风险能力

大规模用户同时访问a a再给b同步 b如果没有太强抗压能力可能就寄了

生产者消费者模型

在a和b之间放一个阻塞队列  即使a的压力很大 b仍然按照既定的频率来取数据

代码实现

 public static void main(String[] args) {
            BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
            Thread productor = new Thread(()->{
            	int n = 0;
            	while(true) {
            		try {
						System.out.println("生产元素"+n);
						queue.put(n);
						n++;
						Thread.sleep(500);//这行代码,没有实际意义 是为了让运行结果更方便观察而已
					} catch (Exception e) {
						e.printStackTrace();
					}
            	}
            });
            productor.start();
            Thread customer = new Thread(()->{
            	while(true) {
            		try {
						System.out.println("消费元素"+queue.take());				
						Thread.sleep(500);
					} catch (Exception e) {
						e.printStackTrace();
					}
            	}
            });
            customer.start();
	    }

模拟实现一个阻塞队列

class myqueue{
	int[] queue = new int[100];
	int size = 0;
	int head = 0;
	int tail = 0;
	
	public void put(int a) throws InterruptedException{
		synchronized (this) {
			if(size==queue.length) {
				this.wait();
			}
			
			queue[tail] = a;
			tail++;
			if(tail==queue.length) {
				tail = 0;
			}
			size++;
			this.notify();
		}
	}
	public Integer take() throws InterruptedException{
		int ret = 0;
		synchronized (this) {
			if(size==0) {
				this.wait();
			}
			ret = queue[head];
			head++;
			if(head==queue.length) {
				head=0;
			}
			size--;
			this.notify();
			return ret;
		}
	}
}

不要担心这个notify唤醒的是当前功能的wait 因为如果有wait就做不到这一步

定时器 

使用方法是这样的

先实例化一个Timer对象

然后调用它的schedule方法

schedule有两个参数 第一个参数是Timertask对象 重写里面的run 就是业务代码

第二个参数是时间 一个整形 单位ms

public static void main(String[] args) {
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {			
				@Override
				public void run() {
					System.out.println("任务");
								}
			}, 1000);
	    }

你可能会问那我直接用sleep多好

sleep把线程阻塞了 什么都干不了 但是定时器等待的过程中还可以干别的

command表示任务 after表示暂停多久

这个schedule(时间表) 这是个表啊 也就是说可以放入多个元素

模拟实现

Timer 内部要组织很多任务

还需要有一个线程 通过这个线程来扫描定时器内部 看看哪个时间到了该执行了

使用优先级队列来组织这些任务 使用优先级队列可以排序时间

System.currentTimeMillis()系统当前时间戳\

PriorityQueue<MyTask> queue

这个队列会被多个线程同时访问

schedule可能在多线程中被调用 每次调用都要往队列里添加元素

内部还需要有专门的线程执行任务

涉及到出入 所以不安全

PriorityBlockingQueue用阻塞队列

public void schedule()

进入构造就创建一个线程 循环不断尝试获取队首元素并且判定元素是否可以就绪(大量的产生循环 空转 没有实际性的任务)

所以 在判断结束(不可就绪)后 就让他等一会(sleep 不行 即使可以就绪了sleep还在休息 可以用wait wait可以提前唤醒)(每次插入任务都唤醒判断一下)

都统一的时间戳 如果规定时间比当前时间大(没到时间呢)就塞回去 

如果到了 就执行

优先级队列需要定义优先原则 实现Comparable接口 并重写compareTo 所以要在被比较元素的类里面写

这个return 谁减谁 可以写代码试( 如果第一个参数小于第二个参数,就返回一个负数,如果等于就返回0,如果大于就返回一个正数。而且要求返回int 还要强转)

private Object locker = new Object();

wait 和 notify 都是java.util里面的文件 创建一个object类就可以

原则上来讲 只要判断队首元素就可以(等待时间最少)   但是要防止新加入的元素比队首时间还小 (所以加了notify 所以说用sleep不行) 每次加入新元素都唤醒线程一下 如果这个新元素的时间比之前的时间要早 那就变成按照新的队首元素判断 如果比之前的首元素时间要晚也无所谓 接着等呗

 一个误区

这里这个synchronized 用的是locker作为对象 但是queue.put没有使用locker的资源 是不是就锁不住了? 并不是 它既然锁住了locker 那么其他线程在运行代码块的时候获取不到locker(已经被锁住) 还是会阻塞 所以达成了目的

线程池

进程太重量了 创建和销毁成本较高

线程 就是针对上述问题的优化(公用一组系统资源)

但是更频繁的话 线程也扛不住了

进一步优化

1.线程池

2.协程(纤程) 轻量级线程 

八线程创建好 放在池里 需要使用直接从线程池里面取 用完还回去 就不需要创建和销毁

 相当于十个线程去运行这个一百个任务

模拟实现

这个线程是持续运行的 只要submit有东西输入进去了 就会把它运行

 

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

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

相关文章

[Java基础]—SpringBoot

Springboot入门 Helloworld 依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version> </parent><dependencies><depend…

软件测试基础知识整理(四)- 软件开发模型、测试过程模型

目录 一、软件开发模型 1.1 瀑布模型 1.1.1 特点 1.1.2 优缺点 1.2 快速原型模型&#xff08;了解&#xff09; 1.2.1 特点 1.2.2 优缺点 1.3 螺旋模型&#xff08;了解&#xff09; 1.3.1 特点 1.3.2 优缺点 二、测试过程模型 2.1 V模型&#xff08;重点&#xff…

LeetCode_29. 两数相除

目录 题目描述 思路分析 我的题解 题目描述 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff…

8个免费的高质量UI图标大全网站

UI图标素材是设计师必不可少的设计元素。 高质量的UI图标会让设计师的设计效率事半功倍。 本文分享8个免费的高质量UI图标大全网站。 即时设计资源社区 即时设计资源广场中精选了多款专业免费的UI图标设计资源&#xff0c;无需下载即可一键保存源文件&#xff0c;同时还提供…

深入浅析Linux Perf 性能分析工具及火焰图

Perf Event 子系统 Perf 是内置于 Linux 内核源码树中的性能剖析&#xff08;profiling&#xff09;工具。它基于事件采样的原理&#xff0c;以性能事件为基础&#xff0c;支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位…

Maven PKIX path building failed 错误提示

最近公司的项目突然出现了下面的提示。 PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target -> [Help 2]问题和解决 出现上面的提示的问题是因为 SSL 签名的问题。 …

经典面试题:理解Cookie和Session之间的区别

文章目录 一、Cookie概念先知1、Cookie是什么&#xff1f;2、Cookie从哪里来&#xff1f;3、Cookie要存到哪里去&#xff1f;4、Cookie是存在哪里的&#xff1f;5、浏览器是如何通过Cookie来记录的&#xff1f;6、Cookie的过期时间有什么用&#xff1f; 二、见见Cookie三、会话…

软件设计师考试笔记,已通过

目录 系统可靠度 外部实体 内聚类型 编译过程 逆波兰式 前驱图 scrum框架模型 编译和解释 有限自动机 聚簇索引和非聚簇索引 二叉树的前序,中序,后序遍历 动态规划贪心算法 算法 01背包问题 系统可靠度 1. 串联部件可靠度 串联部件想要这条路走通&#xff0c;只有…

软件测试行业7年了,薪资从10k到了22k,感觉到头了?

蜕变之前 明天的希望&#xff0c;让我们忘了今天的痛苦。 怎样区别一个废柴和一个精英&#xff1f;看外貌&#xff0c;看气质&#xff0c;看谈吐&#xff0c;看消费… 有人忙着把人和人进行分类&#xff0c;有人忙着怎么从这一阶层过渡到上一阶层。当你很累的时候&#xff0c…

引入外部文件实现步骤

1.引入数据库相关依赖 2.创建外部属性文件&#xff0c;properties格式&#xff0c;定义数据信息&#xff1a;用户名 密码 地址等 3.创建spring配置文件&#xff0c;引入context命名空间&#xff0c;引入属性文件&#xff0c;使用表达式完成注入 <beans xmlns"http://w…

交友项目【集成环信Api】

目录 1&#xff1a;自动装配 2&#xff1a;查询用户环信账户 3&#xff1a;环信ID查询用户信息 1&#xff1a;自动装配 在项目中集成环信API&#xff0c;完成即时通信等 环信官方文档地址&#xff1a;Java Server SDK [IM 开发文档] 自动装配模块&#xff1a; pom文件相关…

2.数据结构期末复习之顺序表和链表

1.表是可以是线性结构 学号姓名19(数据项)jams(数据项)20(数据项)ming(数据项) 19 jams或 20 ming是数据元表单个的是数据项‘’线性结构可以表示为 19 jams->20 ming2.什么是逻辑结构?:具有相同类型的有限序列(元素排序的位置,排兵布阵操作的方法) a1 a2 a3 .... an (空…

jenkins流水线使用入门示例

之前采用Jenkins的自由风格构建的项目&#xff0c;每个步骤流程都要通过不同的方式设置&#xff0c;并且构建过程中整体流程是不可见的&#xff0c;无法确认每个流程花费的时间&#xff0c;并且问题不方便定位问题。 Jenkins的Pipeline可以让项目的发布整体流程可视化&#xf…

低代码开发大势所趋,这款无代码开发平台你值得拥有

文章目录 什么是低代码iVX和其它低代码的平台的区别没有创新的“拼凑”&#xff0c;没有好东西iVX在线编辑器体验 什么是低代码 低代码&#xff08;Low Code&#xff09;是一种可视化的应用开发方法&#xff0c;用较少的代码、以较快的速度来交付应用程序&#xff0c;将程序员…

ElasticSearch漫游 (1.安装ELK)

前期准备&#xff1a; 请搭建好linux环境 推荐使用centos7系统请关闭linux防火墙请安装好docker 安装ES 创建网络 我们需要部署kibana容器&#xff0c;因此需要让es和kibana互联&#xff0c;这里先创建一个网络。 docker network create es-net加载es镜像 运行docker命令 部…

智能无线温振传感器:提高锂电设备故障诊断精度的利器

当今锂电工厂对于设备可靠性和生产效率的要求越来越高&#xff0c;而设备故障诊断是其中非常重要的一环。针对锂电设备的振动和温度等健康状态的监测&#xff0c;智能无线温振传感器是一款非常有用的工具。 图.太阳能面板生产&#xff08;iStock&#xff09; 智能无线温振传感器…

和数组处理有关的一些OJ题(JAVA)(ArrayList)

1、给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须确保时间复杂度为O(N)&#xff0c;空间复杂度为O&#xff0c;并原地修改输入数组。元素的顺序可以改…

Android系统原理性问题分析 - Android Java框架层的结构

声明 在Android系统中经常会遇到一些系统原理性的问题&#xff0c;在此专栏中集中来讨论下。Android系统&#xff0c;为了能够更好的理解Android的Java世界的运行规律&#xff0c;此篇分析Android Java框架的结构。此篇参考一些博客和书籍&#xff0c;代码基于Android 7.1.1&a…

资产处置求变,京东拍卖如何做好“价值枢纽”?

近年来&#xff0c;随着资产处置市场规模快速成长以及互联网行业飞速发展&#xff0c;金融资产、司法拍卖、罚没物资等处置方式从最初单纯线下拍卖逐渐落地互联网&#xff0c;服务专业化程度也在不断提高。为更好适应市场变化&#xff0c;满足不断增长的市场需求&#xff0c;5月…

NISP二级证书含金量如何

国家信息安全水平考试&#xff08;National Information Security Test Program&#xff0c;简称NISP&#xff09;&#xff0c;是由中国信息安全测评中心实施培养国家网络空间安全人才的项目。 为培养更多优秀的实践型网络安全人才&#xff0c;中国信息安全测评中心推出了国家…