java并发编程——锁机制

一世执手 提交于 2020-04-11 14:21:09
第一部分:synchronized和volatile 锁机制用来保护对象的一致性以及操作的原子性,是实现线程安全的重要手段。线程安全涉及到对象两个重要的状态:共享性和可变性。如果对象是不可变的、线程私有的那么它一定是线程安全的。所以说,只有在共享的、可变的对象上面进行操作时才需要加锁,以保障线程安全。volatile和synchronized是java 5.0之前最早协调对象共享的机制。下面我们将分别介绍他们: synchronized synchronized用来形容方法或者代码块,是java提供的最早的锁机制,支持重入锁。关于synchronized的详细解析文章有很多,这里列举几个注意事项: 第一,使用synchronized关键字时一定要尽可能的缩小范围,尽可能的在方法块里需要锁的地方使用,而不是直接用来修饰整个方法。这需要我们对方法里面的操作进行分析,哪些需要加锁,哪些不需要加锁,只在需要锁的地方加锁,这样即可以提高程序的效率,同时开放调用方法也减少了线程安全的隐患。 第二,synchronized提供的锁机制是粗粒度的,当有线程访问当前对象的synchronized方法或代码块时,其他线程只能等待当前操作结束才可以访问。这听上去似乎存在一定的性能问题,但java 6.0以后synchronized在并发环境下性能得到了大幅提升

自旋锁学习系列(3):指数后退技术

可紊 提交于 2020-03-23 21:48:51
3 月,跳不动了?>>> 上一篇 中分析了测试锁的两种实现TASLock和TTASLock,主要对这两种锁的性能进行了分析。对于TTASLock,我们知道比TASLock性能上要好很多,具体分析已经讲过了。我们最后也说了,TTASLock虽然比TASLock大有改进,但是在性能上还是不够理想。这一篇的目的就是针对TTASLock做一下改进。 我们再来看一下TTASLock的实现源码和加锁的流程图: /** * * Test test and set lock * */ public class TTASLock { private AtomicBoolean state = new AtomicBoolean(false); // 加锁 public void lock() { while (true) { while (state.get()) { // 自旋 } if (!state.getAndSet(true)) { break; } } } // 解锁 public void unlock() { state.set(false); } } 加锁流程图如下: 从上文我们知道,对于TTASLock锁,性能问题主要出现在解锁上。一旦一个已经获得锁的线程执行解锁操作。其他线程都会产生缓存缺失,将会由”本地自旋”转变为从共享服务器中去获取状态值。这会消耗大量的总线资源。所以

自旋锁学习系列(2):TAS锁

为君一笑 提交于 2020-03-23 21:00:26
3 月,跳不动了?>>> TAS 是test and set 的缩写,直白的翻译过来就是比较然后测试。java中的原子类大量使用了TAS操作。通过TAS 我们可以安全并且无阻塞的设置原子变量,不用加锁也能进行线程安全的操作。本文目的不是谈原子变量的使用和实现原理的,这个以后会单独来讲。我们主要来看如何使用TAS操作来实现互斥锁。 首先让我们来看看最简单的一种实现TASLock,废话少说直接上代码: //test and set lock public class TASLock { private AtomicBoolean state = new AtomicBoolean(false); // 加锁 public void lock() { while (state.getAndSet(true)) { } } // 解锁 public void unlock() { state.set(false); } } 这应该是互斥锁的最简单的实现了吧,加锁和解锁只需要一行有效代码。让我们详细来分析一下TASLock类。TASLock有一个AtomicBoolean类型的字段state,我们用这个字段来标示锁的状态,初始值为false。state为true说明锁已经被某个线程占有,fase则说明锁空闲。AtomicBoolean是布尔值的一个原子类型实现类

Java多线程学习:wait与notify方法的使用

老子叫甜甜 提交于 2020-03-04 17:12:14
在java多线程编程中,最被经常用到的便是wait与notfiy方法,这两个方法可以用来更加精确地控制被同步的代码,从而使得被同步的代码最小化,提高并发效率。 当某个类的某个方法被标记为synchronized时,这个方法在同一时间只能被一个线程访问。此时这个方法中的所有代码都是被同步的,只有当一个线程执行完所有的代码之后,下一个线程才能开始执行。当被同步的方法代码量比较小,而且每一步执行都非常快的时候仅仅使用synchronized关键字就够了。但是,如果被同步的方法里面有一些代码是可以被共享的,而且这些能够被共享的代码里面存在比较耗时的操作时,仅仅使用synchronized关键字就无法达到最高的效率,这个时候可以使用wait与notify方法来对并发访问做更进一步的控制。首先看两段代码: public class TestThread { private boolean isIdle = true; public synchronized void work(){ /* * Some work which can be shared */ try { /* * to check if we can have this object's monitor */ if(!isIdle){ System.out.println(Thread.currentThread()

Linux下线程pthread以及锁的一些总结和代码参考

大城市里の小女人 提交于 2020-03-01 04:58:55
对于linux下线程pthread的认识以及锁的相关概念等等,作为小白的我推荐这一篇比较好的文章,也谢谢大牛的分享: http://casatwy.com/pthreadde-ge-chong-tong-bu-ji-zhi.html 对于只使用基本mutex的同学,摘录文章中的一段话,共同勉励,一起养成良好的编码规范: 如果要进入一段临界区需要多个mutex锁,那么就很容易导致死锁,单个mutex锁是不会引发死锁的。要解决这个问题也很简单,只要申请锁的时候 按照固定顺序,或者及时释放不需要的mutex锁就可以。这就对我们的代码有一定的要求,尤其是全局mutex锁的时候,更需要遵守一个约定。 如果是全局mutex锁,我习惯将它们写在同一个头文件里。 一个模块的文件再多,都必须要有两个umbrella header file。一个是整个模块的伞,外界使用你的模块的时候,只要include这个头文件即可。另一个用于给模块的所有子模块去include,然后这个头 文件里面就放一些公用的宏啊,配置啊啥的,全局mutex放在这里就最合适了。 这两个文件不能是同一个,否则容易出循环include的问题。如果有人写 模块不喜欢写这样的头文件的,那现在就要改了。 然后我的mutex锁的命名规则就是: 作用_mutex_序号 ,比 如 LinkListMutex_mutex_1 ,

ORACLE关于锁表查询的部分SQL

家住魔仙堡 提交于 2020-01-10 11:57:52
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> -- 查询表空间名称和大小 SELECT UPPER (F.TABLESPACE_NAME) "表空间名", D.TOT_GROOTTE_MB "表空间大小(M)", D.TOT_GROOTTE_MB - F.TOTAL_BYTES "已使用空间(M)", TO_CHAR (ROUND ((D.TOT_GROOTTE_MB - F.TOTAL_BYTES) / D.TOT_GROOTTE_MB * 100, 2), '990.99') "使用比", F.TOTAL_BYTES "空闲空间(M)", F.MAX_BYTES "最大块(M)" FROM ( SELECT TABLESPACE_NAME, ROUND (SUM (BYTES) / (1024 * 1024), 2) TOTAL_BYTES, ROUND (MAX (BYTES) / (1024 * 1024), 2) MAX_BYTES FROM SYS.DBA_FREE_SPACE GROUP BY TABLESPACE_NAME) F, ( SELECT DD.TABLESPACE_NAME, ROUND (SUM (DD.BYTES) / (1024 * 1024), 2) TOT_GROOTTE_MB FROM SYS.DBA_DATA

PostgreSQL数据库日常学习笔记17-事务和锁

China☆狼群 提交于 2020-01-07 20:15:12
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> PostgreSQL中提供多种机制保证数据完整性。例如约束、触发器、事务和锁管理等。 PostgreSQL一个特点是并发控制机制,在维护一致性和完整性时,尽量避免堵塞读写。 在PostgreSQL中,使用多版本并发控制(MVCC)维护数据一致性,相对于锁定模型,多版本控制系统主要优点是读(检索)数据和写数据锁请求不互相冲突,读写不互相阻塞。传统数据库,为维护数据一致性和完整性,避免一个事务读写到其它并发事务更新所产生不一致数据,通常采用就会使用LOCK机制。付出成本就是,当锁请求无法被响应时,待处理请求必须进入等候队列,甚至等待超时不被处理。 在日常数据库操作中,一般情况下我们对于一组操作通常希望能够全部成功或失败,即不允许部分成功情况发生。一组操作,要么全部执行完毕,要么完全执行失败,类似操作被称为事务。 数据库管理系统(DBMS)中,事务(transaction)具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),合并缩写为ACID。. 原子性是指事务是一个不可再分割工作单元,事务中全部操作要么全部执行,要么都不执行,如果中途出现错误,需要回滚已完成所有操作,让数据恢复到未执行操作前状态。

mysql并发插入重复数据问题的解决思路

爷,独闯天下 提交于 2019-12-01 06:01:26
一、问题描述 涉及到的功能是一个表单页面添加数据,后端接口的功能就是往数据库中写入表单的数据,系统属于后台系统,但因为跟钱有关,添加的数据要保证同一次填写的数据只能入一次库。 测试很负责任,在测试保存时开启全部能量狂点保存按钮,结果出现了多条一样的数据,问题由此产生 分析问题,原因有两个:1. 表单没有做防重复提交,导致出现多个请求,2. 后端保存接口并发时无法判断同一表单的请求。 二、过程 Round 1:在表单提交后,把按钮置为disabled FAIL:这种解决方案太依赖前端,并且无法保证刷新时重复提交表单 Round 2:做表单防重复提交,在页面打开时,在session和页面中设置token,表单提交时判断token,并删除session中的token FAIL:目前的项目中没有先例,要解决防重复提交的问题应考虑整个项目的处理机制,不应特殊化某一功能,对后期维护不好,同时多个页面的token在session中也需要有不同的key,而项目部署时有多个实例,用session实现需要太多的前提条件 Round 3:既然不通过防重复提交解决,那就解决并发问题吧,java中提供了java本身就提供了synchornized和Lock,不仅如此,mysql也提供了get_lock,release_lock方法,用以获取和释放锁 FAIL

mysql事务隔离级别和锁

扶醉桌前 提交于 2019-11-30 04:33:15
事务的核心是锁和并发,采用同步控制的方式保证并发的情况下性能尽可能高,且容易理解。 一、 事务有四个基本特性: 1、原子性(atomicity):数据库操作的最小基本单位,事务内的操作要么都成功,要么都失败,中途不能被打断。 2、一致性(consistency):事务执行后数据从一种状态变成另外一种状态,不会存在事务的一部分数据写入了数据库,一部分没有写入。 3、隔离性(isolation):事务之间独立执行,不相互干扰。 4、持久性(durability):事务一旦提交,对数据库的修改是永久性的,不会改变。 二 、事务隔离 当多个线程同时执行读、算、写操作时,如果不加访问控制,系统势必会产生冲突,举例说明: 并发的时候,事务不断执行,这段时间简称 T 。T上有很多时间点, T0 , T1 , T2 , T3 , T4 , T5 .......... T0 为时间起点,数据库上有一个表user,字段name,point。 事务A:select name,point from user where id =1; 事务B:update user set point=point+1 where id =1; 脏读 :首先事务B开始执行,在T1的时候事务A又开始执行,这个时候B事务并没有提交,事务A查询的值是事务B未提交的值,如果此时B事务回滚,会造成事务A查询到错误数据。这就是脏读。

synchronized和reentrantlock到底锁了什么?

做~自己de王妃 提交于 2019-11-28 22:56:04
第一个类,在add clear等方法里使用可重入锁 public class LockList extends ArrayList<Object>{ private Lock lock = new ReentrantLock(false); @Override public boolean add(Object o) { lock.lock(); try{ System.out.println(Thread.currentThread().getName()+"add lock"); return super.add(o); }finally { System.out.println(Thread.currentThread().getName()+"add unlock"); lock.unlock(); } } @Override public void clear() { lock.lock(); try{ System.out.println(Thread.currentThread().getName()+"clear lock"); super.clear(); }finally { System.out.println(Thread.currentThread().getName()+"clear unlock"); lock.unlock(); } }