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

这篇具有很好参考价值的文章主要介绍了Java-线程安全的四个经典案例和线程池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单例模式

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

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

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

饿汉模式:

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

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不行) 每次加入新元素都唤醒线程一下 如果这个新元素的时间比之前的时间要早 那就变成按照新的队首元素判断 如果比之前的首元素时间要晚也无所谓 接着等呗

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

 一个误区

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

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

线程池

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

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

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

进一步优化

1.线程池

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

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

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

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

模拟实现

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

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

 文章来源地址https://www.toymoban.com/news/detail-450488.html

到了这里,关于Java-线程安全的四个经典案例和线程池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 线程的四个属性

    如上图所示,线程有四个属性: 线程ID 线程名称 守护线程 线程优先级 每个线程都有id,这个id不能修改 线程id会不停的自增,从1开始 main函数就是第一个线程,id=1 id 是操作系统用来识别各个线程的编号,具有唯一性,从下面 java 的源码中看到,这个线程的Id初始值是0,但是

    2024年02月07日
    浏览(49)
  • 路由器故障排错三大经典案例

    对于网络管理员来说,熟悉与掌握路由排错的思路和技巧是非常必要的。小编将通过三例典型的路由故障排错案例进行分析。 案例1 不堪重负,路由器外网口关闭 1、网络环境 某单位使用的是Cisco路由器,租用电信30MB做本地接入和l0MB教育网双线路上网,两年来网络运行稳定,

    2024年02月05日
    浏览(46)
  • 国内外大数据经典案例研究

    大数据时代的来临使得产生的数据量呈爆炸式增长,各行各业均面临着海量数据的分析、处理问题。如何运用大数据技术从海量数据中挖掘出有价值的信息,将是今后企业发展的一个巨大挑战。点评收集研究了国内外大数据应用的经典案例,希望可以对读者有所启示。 1 、塔

    2024年02月05日
    浏览(73)
  • 经典智能合约案例之发红包

    角色分析:发红包的人和抢红包的人 功能分析: 发红包:发红包的功能,可以借助构造函数实现,核心是将ether打入合约; 抢红包:抢红包的功能,抢成功需要一些断言判断,核心操作是合约转账给抢红包的人; 退还:当红包有剩余的时候,允许发红包的人收回余额,可以

    2024年02月07日
    浏览(44)
  • Python递归的几个经典案例

    当我们碰到诸如需要求阶乘或斐波那契数列的问题时,使用普通的循环往往比较麻烦,但如果我们使用递归时,会简单许多,起到事半功倍的效果。这篇文章主要和大家分享一些和递归有关的经典案例,结合一些资料谈一下个人的理解,也借此加深自己对递归的理解和掌握一

    2024年02月05日
    浏览(44)
  • 阿里后端开发:抽象建模经典案例

    在互联网行业,软件工程师面对的产品需求大都是以具象的现实世界事物概念来描述的,遵循的是人类世界的自然语言,而软件世界里通行的则是机器语言,两者间跨度太大,需要一座桥梁来联通,抽象建模便是打造这座桥梁的关键。基于抽象建模,不断地去粗取精,从现实

    2024年02月09日
    浏览(87)
  • MySQL学习指南&笔记&经典案例句

    该文章是一篇关于MySQL的一个学习的笔记或是指南,该文章中有很多的经典的案例可进行相应的练习和参考,后期的话会持续更新关于数据库系统方面的文章。 关于综合案例的话可以对该篇文章进行查阅和学习也附加了相应的问题和sql句: MySQL综合应用练习(直接拷贝到自己空

    2024年02月05日
    浏览(47)
  • 【入门Flink】- 02Flink经典案例-WordCount

    需求:统计一段文字中,每个单词出现的频次 基本思路:先逐行读入文件数据,然后将每一行文字拆分成单词;接着按照单词分组,统计每组数据的个数。 1.1.数据准备 resources目录下新建一个 input 文件夹,并在下面创建文本文件words.txt words.txt 1.2.代码编写 打印结果如下:(

    2024年02月06日
    浏览(71)
  • C#中的反射(Reflection)使用经典案例

    C#中的反射(Reflection)是.NET框架提供的一种强大的运行时元编程机制,它允许程序在运行时获取类型信息、创建对象实例、调用方法、访问字段和属性等,而这些操作在编译时可能是未知的。以下是几个使用反射的典型场景: 1. 动态加载和调用类的方法 假设有一个库包含多

    2024年02月02日
    浏览(43)
  • C++动态规划经典案例解析之合并石子

    区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。 如 前缀和 就是极简单的区间问题。如有如下数组: 现给定区间信息 [3,6] ,求区间内所有数字相加结果。即求如下图位置数字之和。 Tips: 区间至少包括

    2024年02月11日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包