首页 > 编程语言 >[Java]Java初学之多线程03--同步与锁

[Java]Java初学之多线程03--同步与锁

时间:2023-10-28 19:33:46浏览次数:29  
标签:03 account 同步 Java synchronized 线程 new 多线程 public

Intro

本篇文章主要关于多线程"同步"以及"锁"的相关内容~

正文

同步(Synchronize)

概念

“同步”是基于“并发”的需求而出现的
所谓并发,就是同一个对象被多个线程同时操作,比如两个人同时从同一个账户取钱,再比如春运抢票。

多个线程同时使用一个资源,必然会造成混乱。想象一下从前的线下购票厅,如果大家都不排队而拥挤着抢票,不仅流程混乱,更可能少票少钱。而排队则可以让整个流程有序顺畅进行。

同理,同步是一种等待机制,多个需要同时访问同一个对象的线程会进入对象的等待池形成队列,等前面的线程使用资源完毕释放后,下一个线程再使用。使用期间怎么做到防止其他线程访问,则是在线程访问时加入锁机制,如此便可独占资源。释放的过程也就是解锁的过程。
因此,同步其实就是排队+锁

但是,鱼和熊掌不可兼得,保证了安全性,必然会牺牲一定的性能,锁存在以下问题:

  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起
  2. 在多线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延时
  3. 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置

实现

要实现同步,可以通过Synchronized关键字实现,它包括了两种用法:synchronized方法synchronized块

接下来用两个不同示例分别示范Synchronized用法~
Synchronized方法:在方法前加synchronized前缀

//假设我们在抢演唱会的票
package com.multiThread;
public class DemoTicket {
	public static void main(String[] args) {
		BuyTicket concertHins = new BuyTicket(); //买张敬轩演唱会的票w
		//假设有三个人在抢票
		new Thread(concertHins, "Fans-1").start();
		new Thread(concertHins, "Fans-2").start();
		new Thread(concertHins, "Fans-3").start();
	}
}
//线程BuyTicket
class BuyTicket implements Runnable {
	private int ticketNums = 100; //我们假设有100张票
	boolean flag = true //设定一个标志
	@Override
	public void run() {
		while (flag) {
			try {
				buy(); //调用买票方法
			}catch (InterruptedException e) {
				throw new RuntimeException(e)
			}
		}
	}
}
//在涉及多线程增删改的方法前增加前缀synchronized,使其变成同步方法
private synchronized void buy() throws InterruptedException {
	if (ticketNums <= 0) {
		flag = false;
		return; //没票就设置标志为false 结束循环
	}
	System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--)
}

结果会根据每个人电脑配置不同而不同,就不展示了,但一定是顺序并且不会有小于零的情况。

不妨思考一下,synchronized前缀是给谁上了锁呢?
是这个方法吗本身?是增删改的属性吗?

点击查看思考~
其实是**锁上了拥有这个方法的对象**,在这个示例中则是**BuyTicket**

可以再想想,如果不加synchronized前缀,会怎样?
如果没有synchronized前缀,输出就不会是顺序的,甚至可能几个Fans拿到同一张票,也可能拿到-1张票

Synchronized块:对代码片段用synchronized块包裹起来

//假设你和你朋友想从同一个银行账户取钱
package com.multiThread;
public class DemoMoney {
	public static void main(String args[]) {
		Account account = new Account(100, "基金");
		Drawing you = new Drawing(50, "你");
		Drawing friend = new Drawing(100, "朋友");
		you.start();
		friend.start();
	}
}
//创建账户对象
class Account {
	int money;
	String name;
	//有参构造
	public Account(int money, String name) {
		this.money = money;
		this.name = name;
	}
}
//
class Drawing extends Thread {
	Account account;
	int drawingMoney;
	int currentMoney;
	public Drawing(Account account, int drawingMoney, String name) {
		super(name); //从Thread执行中继承名字
		this.account = account;
		this.drawingMoney = drawingMoney;
		}
	
	@Override
	public void run() {
		//在对属性增删改的代码片段中用synchronized块包裹,参数是同步对象
		//我们实际是对account操作,因此同步对象是account
		synchronized (account) {
			if (account.money - drwaingMoney < 0) {
			System.out.println(Thread.cuurentThread().getName() + "金额不足")
			return;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedExpection e) {
				throw new RuntimeException(e);
			}
			account.money = account.money - drawingMoney;
			currentMoney = currentMoney + drawingMoney;
			
			System.out.println(account.name + "余额为" + account.money);
			//this.getName就是Thread.currentThread().getName();
			System.out.println(this.getName() + "手里的钱" + currentMoney)
		}
	}
}

从代码可以看出来,you线程先开始,因此结果是你可以取到钱,之后账户余额不足,你朋友就取不到了

进一步思考一下,这个可不可以用方法级别的synchronized实现呢?
答案当然是可以,但,不是在run方法前写synchronized前缀,因为这样锁的就不是account了

点击查看思考~
答案当然是可以,但,不是在run方法前写synchronized前缀,因为这样锁的就不是account了
可以在account里写同步方法然后在run中调用

Ending

我不想把每一篇文章写得太长,因此这篇文章到这里就结束吧~
总结一下:本文主要介绍了同步的概念,从what why how 三个点来展开同步这个概念
用两个示例分别展示了方法级别的synchronized块级别的synchronized
以及其中锁的细节

标签:03,account,同步,Java,synchronized,线程,new,多线程,public
From: https://www.cnblogs.com/zoexcode/p/17794394.html

相关文章

  • 如何用JavaScript更改元素的类?
    内容来自DOChttps://q.houxu6.top/?s=如何用JavaScript更改元素的类?我该如何使用JavaScript响应onclick或其他事件来更改HTML元素的类?现代HTML5技术用于更改类现代浏览器添加了classList,它提供了更方便地操作类的方法,而无需使用库:document.getElementById("MyElement").c......
  • JavaFrame
    1.课程回顾在本人大三时修了JavaWeb编程和Java框架编程,这两门的课程结构大致是这样:JavaWeb:Java框架:Web开发基础Maven工具Servlet基础Spring框架ServletAPI核心接口SpringMVC会话跟踪数据持久化技术数据访问与JavaBeanBootstrap,Javascript,Iframe,Ajax......
  • rust 创建多线程web server
    创建一个httpserver,处理http请求。创建一个单线程的web服务webserver中主要的两个协议是http和tcp。tcp是底层协议,http是构建在tcp之上的。通过std::net库创建一个tcp连接的监听对象,监听地址为127.0.0.1:8080.usestd::net::TcpListener;fnmain(){l......
  • Java Hotspot G1 GC 原理
    目录原理概念初始堆占用情况标记RememberSet原理CardTableCollectSet停顿预测模型G1的垃圾回收过程对象分配线程本地分配缓冲区Eden区中分配Humongous区分配堆内存结构传统的GC收集器G1收集器G1垃圾收集周期YoungGCYoungGC总结MixedGC全局并发标记初始标记根区域扫描......
  • 前端多线程处理——async/await
    async从字面上看就是“异步”,它放在函数定义之前,是使该函数在调用时开一个子线程,以不影响主线程的运行。而await经常和async组合使用,在async定义的函数中来等待需要时间运行的代码(如ajax请求、Promise对象)的运行结果,以做后续的处理。  如下面的返回Promise对象......
  • 使用pandas,Missing optional dependency 'xlrd'. Install xlrd >= 2.0.1 for xls Exce
    遇到问题使用pandas处理excel数据,报错:ImportError:Missingoptionaldependency'xlrd'.Installxlrd>=2.0.1forxlsExcelsupportUsepiporcondatoinstallxlrd.解决方案是xlrd版本不匹配,手动安装xlrd......
  • 根据ip获取位置信息java
    通常情况下根据内网ip很难获取到位置信息然而根据外网ip获取相对来说比较简单那么如何获取外网ip呢?很简单有很多种方式第一种是通过命令行的方式第二种是通过直接访问该地址https://api.ipify.org/获取到之后直接调用现成的api,替换成自己的外网ip即可获取到位置信息并不是所有的......
  • 一文掌握Java Stream API
    引言JavaStreamAPI自Java8引入以来,已成为处理集合数据的强大工具。它不仅提高了代码的可读性,还优化了性能,使得集合操作变得更加简洁和高效。本文将深入探讨如何利用StreamAPI的常用操作,帮助你更好地掌握这一强大的功能。JavaStreamAPI简介JavaStream是Java8引入的......
  • Java面试专题
    Java面试专题面试题背后的逻辑->拆分问题讲解->回答方式及参考问题Redis篇使用场景1、你在最近的项目中哪些场景使用了redis?缓存:缓存击穿,缓存穿透,缓存雪崩,双写一致性,数据过期策略,数据淘汰策略分布式锁:setnx,redission2、什么是缓存穿透,怎么解决?缓存穿透:查询一个不存在......
  • javaweb--JDBC的API-Connection
    1、获取执行SQL对象2、管理事务setAutoCommit(bool)true为自动提交false为手动提交commit()提交事务rollback()回滚事务packagecom.avb.jdbc;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.SQLException;importjava.sql.Statement;public......