Java笔记(死锁、线程通信、单例模式)

一、死锁

1.概述

  • 死锁 : 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法往下执行。
  • 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
  • 原理 :
    1. 某个线程执行完成,需要先后嵌套锁定两个对象,在这个过程中,先锁定了第一个对象
    2. 另一个线程执行完成也需要先后嵌套锁定两个对象,在这个过程中,先锁定了第二个对象
    3. 第一个线程执行中,要执行到第二个对象的时候,发现第二个对象被锁定,进入等待状态,等待交出锁
    4. 第二个线程执行中,要执行到第一个对象的时候,发现第一个对象也被锁定,也进入等待状态
    5. 此时两个线程都在等待对方交出锁,导致死锁

2.代码实现

public class Thread_01_DeadLock {
	public static void main(String[] args) {
		Object o1=new Object();
		Object o2=new Object();
		Thread t1=new Thread(new T1(o1, o2));
		Thread t2=new Thread(new T2(o1, o2));
		t1.start();
		t2.start();
	}
}
class T1 implements Runnable{
	Object o1;
	Object o2;
	public T1(Object o1,Object o2){
		this.o1=o1;
		this.o2=o2;
	}
	@Override
	public void run() {
		synchronized (o1) {
//			try {//加上睡眠一定死锁
//				Thread.sleep(1000);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
			System.out.println(Thread.currentThread().getName()+"-->T1o1已锁定");
			synchronized (o2) {
				System.out.println(Thread.currentThread().getName()+"-->T1o2已锁定");
			}
		}
		System.out.println("t1执行完成");
	}
}
class T2 implements Runnable{
	Object o1;
	Object o2;
	public T2(Object o1,Object o2){
		this.o1=o1;
		this.o2=o2;
	}
	@Override
	public void run() {
		try {//加上睡眠一定不死锁
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		synchronized (o2) {
			System.out.println(Thread.currentThread().getName()+"-->T2o2已锁定");
			synchronized (o1) {
				System.out.println(Thread.currentThread().getName()+"-->T2o1已锁定");
			}
		}
		System.out.println("t2执行完成");
	}
}

二、线程通信

1.概述

  • Object中的方法
  • wait : 让当前线程进入等待状态(挂起),并释放锁,当被唤醒之后,接着挂起的位置继续执行,假如之前执行了1、2,到3挂起,那么被唤醒后接着执行3
  • notify : 唤醒一个在该对象中挂起的任意一个线程
  • notifyAll : 唤醒在该对象中挂起的所有线程
  • 这几个方法必须出现在加锁的成员方法
  • wait : 如果是无参,则不会自动醒,也可以传入long类型的值,代表毫秒数,多久之后自动醒
  • wait 和 sleep的区别 :
    • sleep : 让当前线程进入睡眠状态, 是静态方法,和是否加锁没有关系,如果在加锁的方法中,也不会释放锁
    • wait : 让当前线程进入挂起等待状态,必须在加锁的成员方法中,另外会释放锁

2.使用方式

public class Thread_03_Wait {
	public static void main(String[] args) throws InterruptedException {
		Num num=new Num();
		Thread t1=new PrintNum(num);
		Thread t2=new PrintNum(num);
		t1.start();
		Thread.sleep(10);//保证t1先执行
		t2.start();
	}
}
class PrintNum extends Thread{
	Num num;
	public PrintNum(Num num){
		this.num=num;
	}
	@Override
	public void run() {
		while (true) {
			num.printNums();
		}
	}
}
class Num{
	private int count =1;
	public synchronized void printNums(){
		System.out.println(Thread.currentThread().getName()+"-->"+count);
		count++;
		// 唤醒等待的进程
		this.notifyAll();
		try {
			Thread.sleep(1000);
			// 进入挂起状态,并释放锁
			this.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

3.生产者消费者

在这里插入图片描述

3.1.示例

public class Thread_04_Producer {
	public static void main(String[] args) {
		SynStack ss=new SynStack();
		Thread producer1=new Thread(new Producer(ss));
		Thread producer2=new Thread(new Producer(ss));
		Thread consumer1=new Thread(new Consumer(ss));
		Thread consumer2=new Thread(new Consumer(ss));
		producer1.start();
		producer2.start();
		consumer1.start();
		consumer2.start();
	}
}
class Producer implements Runnable{
	private SynStack ss;
	public Producer(SynStack ss){
		this.ss=ss;
	}
	@Override
	public void run() {
		for (int i = 0; i < 26; i++) {
			ss.push((char)('a'+i));
		}
	}
}
class Consumer implements Runnable{
	private SynStack ss;
	public Consumer(SynStack ss){
		this.ss=ss;
	}
	@Override
	public void run() {
		for (int i = 0; i < 26; i++) {
			ss.pop();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//业务类
class SynStack{
	int count=0;// 货物数量
	char[] data=new char[6];// 记录货物数量
	// 生产货物
	public synchronized void push(char ch){
		// 判断货物满没满
		while(count ==data.length){//
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//仓库空了该生产了,此时应该唤醒挂起的消费者
		if (count==0) {
			this.notifyAll();
		}
		data[count++]=ch;
		System.out.println(Thread.currentThread().getName()+"生产了 "+ch+" 还剩 "+count+" 个货物");
	}
	// 消费货物
	public synchronized char pop(){
		// 判断货物空没空
		while(count ==0){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//仓库满了该挂起了,此时应该唤醒挂起的生产者
		if (count==data.length) {
			this.notifyAll();
		}
		char ch=data[--count];
		System.out.println(Thread.currentThread().getName()+"消费了 "+ch+" 还剩 "+count+" 个货物");
		return ch;
	}
}

三、单例模式

public class SingLeton {
	private SingLeton(){
		
	}
	// volatile 防止指令重排
	private volatile static SingLeton singLeton;
	public static SingLeton getInstance(){
        // 多线程可能同时进入
		if (singLeton==null) {
            // 一个线程进入
			synchronized (SingLeton.class) {
				if (singLeton==null) {
                    // 一个线程进入后 对象就不再是null,其他的线程将无法进入
					singLeton=new SingLeton();
				}	
			}
		}
		return singLeton;
	}
}

四、线程池

  • 线程池的作用:
    • 线程池作用就是限制系统中执行线程的数量。
  • 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;
  • 少了浪费了系统资源,多了造成系统拥挤效率不高。
  • 用线程池控制线程数量,其他线程排队等候。
  • 一个任务执行完毕,再从队列的中取最前面的任务开始执行。
  • 若队列中没有等待进程,线程池的这一资源处于等待。
  • 当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了,否则进入等待队列。
  • 为什么要用线程池:
    1. 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
    2. 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)

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

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

相关文章

vusui css 使用,简单明了 适合后端人员 已解决

vusui-cssopen in new window 免除开发者繁复的手写 CSS 样式&#xff0c;让 WEB 前端开发更简单、灵活、便捷&#xff01;如果喜欢就点个 ★Staropen in new window 吧。 移动设备优先&#xff1a; vusui-css 包含了贯穿于整个库的移动设备优先的样式。浏览器支持&#xff1a…

【每日一题】最大合金数

文章目录 Tag题目来源解题思路方法一&#xff1a;二分枚举答案 写在最后 Tag 【二分枚举答案】【数组】【2024-01-27】 题目来源 2861. 最大合金数 解题思路 方法一&#xff1a;二分枚举答案 思路 如果我们可以制造 x 块合金&#xff0c;那么一定也可以制造 x-1 块合金。于…

《合成孔径雷达成像算法与实现》Figure5.18

clc clear close all距离向参数 R_eta_c 20e3; % 景中心斜距 Tr 25e-6; % 发射脉冲时宽 Kr 0.25e12; % 距离向调频率 Fr 7.5e6; % 距离向采样率 Nrg 256; % 距离线采样点数 Bw abs(Kr*Tr); …

【C++干货铺】C++中的IO流和文件操作

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 C语言的输入输出 流是什么&#xff1f; C的IO流 C标准IO流 C文件IO流 文本文件读写 二进制文件的读写 stringstream的简单介绍 将数值类型数据格式化为字…

JS中的try...catch

一、定义和结构 作用&#xff1a;捕获同步执行代码下的异常错误 在没有使用try...catch的情况下&#xff0c;同步代码执行遇到异常会报错&#xff0c;并中断后续代码执行&#xff1b; 在使用try...catch的情况下&#xff0c;同步代码执行遇到异常会抛出异常&#xff0c;并继续…

线性代数----------学习记录

线性代数发展历程 &#xff08;1&#xff09;线性方程组&#xff1a;例如二元一次方程组&#xff1b; &#xff08;2&#xff09;行列式&#xff1a;determinant,克莱默&#xff0c;莱布尼兹&#xff1b; &#xff08;3&#xff09;矩阵&#xff1a;方程个数与未知数的个数可…

【前端工程化】环境搭建 nodejs npm

文章目录 前端工程化是什么&#xff1f;前端工程化实现技术栈前端工程化环境搭建 &#xff1a;什么是Nodejs如何安装nodejsnpm 配置和使用npm 介绍npm 安装和配置npm 常用命令 总结 前端工程化是什么&#xff1f; 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块…

JAVAEE初阶 网络编程(五)

TCP协议 一.TCP协议图二. TCP中的关键协议确认应答后发先至机制引入序号和确认序号 超时重传去重机制 建立连接三次握手 一.TCP协议图 我们可以发现&#xff0c;相比于UDP&#xff0c;TCP协议明显复杂很多&#xff0c;比如32位序号和32位确认序号&#xff0c;4位首都长度&#…

前端面试题-js数据类型-怎么判断是对象还是数组-字符串常用方法-数组常用方法

前端面试题-js部分-js数据类型-怎么判断是对象还是数组-字符串常用方法-数组常用方法 JS数据类型有哪些值类型和引用类型的区别数组的常用方法哪些方法会改变原数组 字符串常用方法对象常用方法怎么判断是对象还是数组 JS数据类型有哪些 数据类型类型描述Number基本类型&#…

ANSYS 2023 下载安装教程,附安装包和工具,轻松安装,无套路

前言 ANSYS是一款融结构、流体、电场、磁场、声场分析于一体的大型通用有限元分析(FEA)软件&#xff0c;能与多数计算机辅助设计软件接口&#xff0c;实现数据的共享和交换&#xff0c;如Creo,NASTRAN、Algor、IDEAS、AutoCAD等. 准备工作 1、Win10及以上系统 2、提前准备好…

防火墙的基础知识点

目录 1. 防火墙的意义&#xff1a; 2. 防火墙分类&#xff1a; 3. 防火墙的发展史&#xff1a; 3.1 包过滤 3.2 应用代理 3.3. 状态检测 3.4. 专用设备 3.4.1 入侵检测系统(IDS) 3.4.2 入侵防御系统(IPS) 3.4.3 防病毒网关 (AV) 3.4.4 Web应用防火墙 (WAF) 3.5. 统…

Netty的解码器和编码器

链路图 一个完整的RPC请求中&#xff0c;netty对请求数据和响应数据的处理流程如下图所示 网络线路中传输的都是二进制数据&#xff0c;之后netty将二进制数据解码乘POJO对象&#xff0c;让客户端或者服务端程序处理。 解码的工具称为解码器&#xff0c;是一个入站处理器InBo…

BAT学习笔记:详解环境变量及其所有创建方法

文章目录 一、初识环境变量二、什么是环境变量三、为什么需要环境变量四、环境变量的分类五、环境变量的设置 一、初识环境变量 1.windows 的搜索框中输入 查看高级系统设置。点击打开系统属性窗口。 2. 在系统属性窗口中&#xff0c;点击右下方的“环境变量”打开环境变量设…

Linux服务器配置与管理(第二次实验)

实验目的及具体要求 目的 1.掌握基于命令行的文件操作 2.掌握基于命令行的目录操作 3.掌握用户账户的命令行操作 4.掌握组账户的命令行操作 5.熟悉磁盘分区操作 6.掌握调整优先级的方法 具体要求 1.掌握基于命令行的文件和目录操作 ①创建测试目录 ②创建文件 ③复…

解析MySQL生产环境CPU使用率过高的排查与解决方案

引言 在生产环境中&#xff0c;MySQL作为一个关键的数据库组件&#xff0c;其性能对整个系统的稳定性至关重要。然而&#xff0c;有时候我们可能会遇到MySQL CPU使用率过高的问题&#xff0c;这可能导致系统性能下降&#xff0c;应用页面访问减慢&#xff0c;甚至影响到用户体…

代码随想录算法训练营第十七天 |110.平衡二叉树,257.二叉树的所有路径,404.左叶子之和(待补充)

110.平衡二叉树 1、题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 2、文章讲解&#xff1a;代码随想录 3、题目&#xff1a; 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二…

前端工程化之:webpack1-6(编译过程)

一、webpack编译过程 webpack 的作用是将源代码编译&#xff08;构建、打包&#xff09;成最终代码。 整个过程大致分为三个步骤&#xff1a; 初始化编译输出 1.初始化 初始化时我们运行的命令 webpack 为核心包&#xff0c; webpack-cli 提供了 webpack 命令&#xff0c;通过…

Go 命令行解析 flag 包之快速上手

本篇文章是 Go 标准库 flag 包的快速上手篇。 概述 开发一个命令行工具&#xff0c;视复杂程度&#xff0c;一般要选择一个合适的命令行解析库&#xff0c;简单的需求用 Go 标准库 flag 就够了&#xff0c;flag 的使用非常简单。 当然&#xff0c;除了标准库 flag 外&#x…

架构整洁之道——价值维度与编程范式

1 设计与架构究竟是什么 结论&#xff1a;二者没有任何区别&#xff0c;一丁点区别都没有。 架构图里实际上包含了所有底层设计细节&#xff0c;这些细节信息共同支撑了顶层的架构设计&#xff0c;底层设计信息和顶层架构设计共同组成了整个架构文档。底层设计细节和高层架构信…

Neo4j 国内镜像下载与安装

Neo4j 5.x 简体中文版指南 社区版&#xff1a;https://neo4j.com/download-center/#community 链接地址&#xff08;Linux版&#xff09;&#xff1a;https://neo4j.com/artifact.php?nameneo4j-community-3.5.13-unix.tar.gz 链接地址&#xff08;Windows&#xff09;&#x…