java解决共享资源竞争
由于多线程的实现,在运行一个程序的时候可能会有很多的线程在同时运行,但是线程的调度并不是可见的,所以不会知道一个线程什么时候在运行,比如说 你坐在桌子前手拿着叉子,正要去叉盘中的最后一片食物,当你的叉子就要够得着他的时候,这片食物突然消失了,因为你的线程挂起了,另一个用餐者进入并吃掉了它,所以这就是在使用多线程时会出现的问题,对于并发的任务,你需要一种方式来防止两个线程同时访问一段资源,基本上所有的并发模式在解决线程冲突上都是采用序列化访问共享资源的方案,这就意味着在cpu给定的时刻,只允许一个线程访问共享资源,通常是在代码前加上一条锁定语句来实现的,一旦执行到锁定语句上的时候,就会产生一种互相排斥的效果,所以这种机制常常成为互斥量,
第一种 关键字synchronized:java为放置资源冲突提供了内置的支持 关键字synchronized,当代码执行到被synchronized保护的代码片段的时候,它会检查锁是否可用,然后获取锁,释放锁。
注意 :* 共享资源一般是以对象形存在的内存片段,但也可以是文件、输入\输出端口,或者打印机,要控制对共享资源的访问,得先把它包进一个对象,然后把所有要访问这个资源的方法标记为synchronized,如果某个线程处于一个对标记synchronized的方法的调用中,那么在这个方法返回前。其他所有要调用这个类中任何一个标记为synchronized的方法的线程都会被阻塞。
synchronized的缺陷:当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,必须等待或者阻塞,这对高并发的系统是致命的,这很容易导致系统的崩溃。如果某个线程在同步方法里面发生了死循环,那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。这是一个致命的问题。
当然同步方法和同步代码块都会有这样的缺陷,只要用了synchronized关键字就会有这样的风险和缺陷。既然避免不了这种缺陷,那么就应该将风险降到最低。这也是同步代码块在某种情况下要优于同步方法的方面。例如在某个类的方法里面:这个类里面声明了一个对象实例,SynObject so=new SynObject();在某个方法里面调用了这个实例的方法so.testsy();但是调用这个方法需要进行同步,不能同时有多个线程同时执行调用这个方法。
这时如果直接用synchronized修饰调用了so.testsy();代码的方法,那么当某个线程进入了这个方法之后,这个对象其他同步方法都不能给其他线程访问了。假如这个方法需要执行的时间很长,那么其他线程会一直阻塞,影响到系统的性能。
如果这时用synchronized来修饰代码块:synchronized(so){so.testsy();},那么这个方法加锁的对象是so这个对象,跟执行这行代码的对象没有关系,当一个线程执行这个方法时,这对其他同步方法时没有影响的,因为他们持有的锁都完全不一样。
所有的对象自动含有单一的锁也叫做监视器,当在对象上调用任意synchronized方法的时候,此对象都被加锁,这时该对象上的其他synchronized方法只有等到前一个方法调用完毕并且释放了所之后才能被调用,还有一个需要注意的 将共享资源设置为private是很有必要的,为了防止其他线程直接访问资源,那么加了锁也没有用,
一个类的对象锁和另一个类的对象锁是没有关联的,当一个线程获得A类的对象锁时,它同时也可以获得B类的对象锁。
然后一个线程可以多次获得对象的锁,比如一个访问的这个被synchronized标记的方法当中在同一个对象上调用了第二个被synchronized标记的方法,后者又调用了另一个同一个对象上被synchronized标记的方法,就会发生这种情况,虚拟机负责跟踪对象被加锁的次数,如果一个对象被完全解锁,其计数器变为0,在线程第一次给对象加锁的时候,计数器变为1,然后每当这个相同的线程在这个对象上获得锁的时候。依次累加1
java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的
先定义一个共享资源的对象 演示一下对象锁

package test.thread.sx;
public class BankAccount {
//余额
private int banlance = 500;
//查询
public int getBalance(){
return banlance;
}
//取款
public void withdraw(int amount){
banlance = banlance - amount;
}
//存款
public void deposit(int amount){
banlance = banlance + amount;
}
}

然后定义任务方法

package test.thread.sx; public class TesMony implements Runnable {
//所有Thread多线程线程都共享Runnable(接口对象)和account对象
private BankAccount account = new BankAccount();
@Override
public void run() {
for(int i = 0; i< 5; i++){ //总共取款5次
makeWithdraw(100); //每次取款100
if(account.getBalance() < 0){
System.out.println("☆"+Thread.currentThread().getName()+" 透支了!");
}
}
} /**
* makeWithdraw 账户取款
* @param amount 取款金额<br />
* 打印log记录取款过程
* */
private synchronized void makeWithdraw(int amount){
if(account.getBalance() >= amount){ //如果余额足够则取款
System.out.println("☆"+Thread.currentThread().getName()+" 准备取款!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" 准备取款,等待0.5s线程中断!"+e.getMessage());
}
account.withdraw(amount);
System.out.println("☆"+Thread.currentThread().getName()+" 完成"+amount+"取款!余额为"+account.getBalance()); }else{ //余额不足则提示
System.out.println("☆"+"余额不足以支付"+Thread.currentThread().getName()+amount+" 的取款,余额为"+account.getBalance());
}
} }

然后测试一下

package test.thread.sx; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Tess { public static void main(String[] args) {
//定义任务的时候 我自己不理解线程的时候犯了一个错误,就是每一个新线程都会给它一个新的任务 也就是新的TesMony对象 那其实就不存在资源竞争了
//因为那根本就是两个对象 资源竞争是针对一个对象来说的 而且互斥锁也是锁在同一个对象上才会出现互斥,
TesMony target = new TesMony();
//创建李琦和他老婆两个线程实现取款(同时)
Thread lq = new Thread(target);
lq.setName("罗密欧");
Thread lqwf = new Thread(target);
lqwf.setName("朱丽叶");
//调用Thread对象的start()方法,启动线程,执行run()方法(OS)
lq.start();
lqwf.start();
}
}

第二种 java又提供了另一种方式显示LOCK的方式 这也是以恶搞被互斥调用的锁,并使用lock和unlock方法标识临界资源,它和synchronized关键字的区别,当我们使用synchronized关键字标记的代码片段出现某些错误的时候 会抛出一个异常 但是不会有机会去做任何的补救工作,但是使用lock对象的方法去加锁 解锁,的时候我们可以使用finnaly字句去做一些处理。那么上面的代码可以这样他修改

package test.thread.sx; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class TesMony implements Runnable {
private Lock lock = new ReentrantLock();
// 所有Thread多线程线程都共享Runnable(接口对象)和account对象 private BankAccount account = new BankAccount(); @Override
public void run() {
for (int i = 0; i < 5; i++) { // 总共取款5次
makeWithdraw(100); // 每次取款100
if (account.getBalance() < 0) {
System.out.println("☆" + Thread.currentThread().getName() + " 透支了!");
}
}
} /**
* makeWithdraw 账户取款
*
* @param amount
* 取款金额<br />
* 打印log记录取款过程
*/
private void makeWithdraw(int amount) {
lock.lock();
try {
if (account.getBalance() >= amount) { // 如果余额足够则取款
System.out.println("☆" + Thread.currentThread().getName() + " 准备取款!");
Thread.sleep(500);
account.withdraw(amount);
System.out.println(
"☆" + Thread.currentThread().getName() + " 完成" + amount + "取款!余额为" + account.getBalance());
} else { // 余额不足则提示
System.out.println("☆" + "余额不足以支付" + Thread.currentThread().getName() + amount + " 的取款,余额为"
+ account.getBalance());
}
} catch (InterruptedException e) {
System.err.println("中断了请等待");
} finally {
lock.unlock();
} } }

java解决共享资源竞争的更多相关文章
- HDC2021技术分论坛:异构组网如何解决共享资源冲突?
作者:lijie,HarmonyOS软总线领域专家 相信大家对HarmonyOS的"超级终端"比较熟悉了.那么,您知道超级终端场景下的多种设备在不同环境下是如何组成一个网络的吗?这 ...
- Golang之并发资源竞争(互斥锁)
并发本身并不复杂,但是因为有了资源竞争的问题,就使得我们开发出好的并发程序变得复杂起来,因为会引起很多莫名其妙的问题. package main import ( "fmt" &q ...
- java线程共享受限资源 解决资源竞争 thinking in java4 21.3
java线程共享受限资源 解决资源竞争 具体介绍请參阅:thinking in java4 21.3 thinking in java 4免费下载:http://download.csdn.net/ ...
- JAVA学习笔记 -- 多线程之共享资源
在多线程程序执行过程中,可能会涉及到两个或者多个线程试图同一时候訪问同一个资源.为了防止这样的情况的发生,必须在线程使用共享资源时给资源"上锁",以阻挡其他线程的訪问. 而这样的机 ...
- 【java项目实战】ThreadLocal封装Connection,实现同一线程共享资源
线程安全一直是程序员们关注的焦点.多线程也一直是比較让人头疼的话题,想必大家以前也遇到过各种各种的问题.我就不再累述了.当然,解决方案也有非常多,这篇博文给大家提供一种非常好的解决线程安全问题的思路. ...
- python中线程共享资源问题的解决
线程跟进程有些相似,有时被称作轻量级的进程,但不同的是,所有的线程运行在同一个进程中,共享相同的运行坏境. 进程和线程都是实现多任务的一种方式,例如:在同一台计算机上能同时运行多个QQ(进程),一个Q ...
- Java使用wait() notify()方法操作共享资源
Java多个线程共享资源: 1)wait().notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写. 2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线 ...
- 关于java多线程任务执行时共享资源加锁的方式思考
1.加锁方式: 1-1.使用synchronized关键字进行方法或代码块的加锁方式 1-2.使用ReentrantLock类提供的lock()方法的方式 2.代码实现(传统的银行取款存款问题): 2 ...
- 9、Java并发性和多线程-线程安全与共享资源
以下内容转自http://ifeve.com/thread-safety/: 允许被多个线程同时执行的代码称作线程安全的代码.线程安全的代码不包含竞态条件.当多个线程同时更新共享资源时会引发竞态条件. ...
随机推荐
- Split Array Largest Sum LT410
Given an array which consists of non-negative integers and an integer m, you can split the array int ...
- vue获取DOM元素并设置属性
这里我想到了2个方法: 方法一: 直接给相应的元素加id,然后再document.getElementById("id");获取,然后设置相应属性或样式 方法二: 使用ref,给相 ...
- 原生js的dom操作
父节点parentNode 第一个子节点 只会获取到元素节点 firstElementChild ★★★★★ 第一个子节点 (如果有文本节点将会获取到文本节点) firstChild 最 ...
- openssl pem文件的读取
准备工作 生成私钥文件(里面已包含公钥) openssl genrsa -out private.pem 1024 从私钥文件中提取公钥 openssl rsa -in private.pem -pu ...
- 通过http.client解析url返回的数据时为什么中文变成了unicode码
今天在解析json数据的时候得到了一堆这样的数据:{"errNum":0,"errMsg":"success","retData& ...
- javaScript正则表达式的使用
今天看了一个正则的写法,回想一下,对于正则都忘记得差不多了,称这个时间整理一下,收集了一些以前的资料和查看了一些别人的资料,做一个小小的总结,方便自己以后查看,也希望能帮助到大家!! 欢迎指正,欢 ...
- B+树和LSM比较(转)
出处:https://blog.csdn.net/u013928917/article/details/75912045 B+树和LSM比较 在关系型数据库mysql中普遍使用B+树作为索引,在实际中 ...
- boost-容器
1.array array相当于是一个增加了STL容器接口的数组,但它不像vector等容器一样可以动态增长,如果需要动态变动array的容量可以使用boost::scoped_array.array ...
- UVa 11021 Tribles (概率DP + 组合数学)
题意:有 k 只小鸟,每只都只能活一天,但是每只都可以生出一些新的小鸟,生出 i 个小鸟的概率是 Pi,问你 m 天所有的小鸟都死亡的概率是多少. 析:先考虑只有一只小鸟,dp[i] 表示 i 天全部 ...
- i2c总线驱动,总线设备(适配器),从设备,从设备驱动的注册以及匹配
常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 随笔分类 ARM裸机(13) C(8) C++(8) GNU-ARM汇编 Linux驱动(24) Linux应用编程(5) Makefile ...