您好、欢迎来到现金彩票网!
当前位置:2019管家婆最准的资料 > 指令重试 >

浅谈 线程安全 和 java锁的分类及实现

发布时间:2019-08-15 05:58 来源:未知 编辑:admin

  什么是线程?是cpu能够进行运算调度的最小单元。它被包含在进程中(进程是系统

  什么是多线程?解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。什么是线程安全呢? 在拥有

  并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行。总结:操作系统为进程分配了资源,比如:地址空间、全局变量等等。线程是进程的一部分,CPU 调度的线 线程生命周期

  BLOCKED:表示线程阻塞,等待获取锁,如碰到synchronized、lock等关键字等占用临界区的情况,一旦获取到锁就进行RUNNABLE状态继续运行。

  WAITING:表示线程处于无限制等待状态,等待一个特殊的事件来重新唤醒,如通过wait()方法进行等待的线程等待一个notify()或者notifyAll()方法,通过join()方法进行等待的线程等待目标线程运行结束而唤醒,一旦通过相关事件唤醒线程,线程就进入了RUNNABLE状态继续运行。

  TIMED_WAITING:表示线程进入了一个有时限的等待,如sleep(3000),等待3秒后线程重新进行RUNNABLE状态继续运行。

  需要注意的是,一旦线程通过start方法启动后就再也不能回到初始NEW状态,线程终止后也不能再回到RUNNABLE状态。

  可以把单线程程序当成在问题域求解的单一实体,每次只能做一件事情。因为只有一个实体,所以永远不会担心诸如“两个实体试图同时使用同一个资源”的问题。

  基本上所有的并发模式在解决线程冲突问题的时候,都是采用序列化访问共享资源的方案。这意味着在给定时刻只允许一个任务访问共享资源,通常这是通过加锁来实现的。

  synchronized关键字和Lock的实现类都是悲观锁。乐观锁:总是假设最好的情况。

  但是在更新数据的时候,会判断一下在此期间,有没有别的线程去更新这个数据。如果没有更新,则将自己要更新的数据写入,如果数据资源已经被别的线程更新过,则执行不同的操作(报错或者重试)。

  乐观锁一般会使用版本号机制或者CAS算法实现。current.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。代码示例:

  一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

  举一个简单的例子:假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。

  操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。

  操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。

  操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。

  这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。

  即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

  当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

  可重入锁,如果外层方法和内层方法都加可重入锁, 同一个线程在外层方法获取锁之后,再进入内层方法会自动获取锁,不会因为之前已经获取过锁还没有释放()而阻塞。

  是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。

  类中的两个方法都是被内置锁synchronized修饰的,doSomething()方法中调用doOthers()方法。因为内置锁是可重入的,所以同一个线程在调用doOthers()时可以直接获得当前对象的锁,进入doOthers()进行操作。

  简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)

  对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。

  对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。

  实现线程同步。synchronized 的锁存在于java 的对象头,对象头中最后两位表示 锁的标志位,对象头中还包含有指向monitor对象的指针,synchronized 的线程同步是通过Monitor 对象来实现的。Monitor 是JVM 通过调用操作系统的互斥原语mutex 来实现的,被阻塞的线程会被挂起,等待重新调度。所以可以理解为:synchronized 最初也是个重量级的锁。但是JVM 又对synchronized 的运行机制做了优化,提供了三种不同的 Monitor 实现,也就是三种常见的锁:偏斜锁(Biased Locking)、轻量级锁和重量级锁。当JVM 检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级和降级。我们来看一下Sychronized 对代码段加锁反编译的过程:

  Monitor可以理解为一个同步工具或一种同步机制,通常被描述为一个对象。每个对象都是一个监视器锁(monitor)。

  。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

  在JVM 中,对象在内存中的布局为三块区域:对象头、实例数据和对齐填充。对象头主要包括两部分数据:Mark Word(标记字段)、Class Pointer(类型指针)。

  对象头的最后两位存储了锁的标志位,01是初始状态,未加锁,其对象头里存储的是对象本身的哈希码,随着锁级别的不同,对象头里会存储不同的内容。偏向锁存储的是当前占用此对象的线程ID;

  。从这里我们可以看到,“锁”这个东西,可能是个锁记录+对象头里的引用指针(判断线程是否拥有锁时将线程的锁记录地址和对象头里的指针地址比较),也可能是对象头里的线程ID(判断线程是否拥有锁时将线程的ID和对象头里存储的线程ID比较)。

  ,每一个线程都有一个可用Lock Record列表,同时还有一个全局的可用列表。每一个被锁住的对象Mark Word都会和一个Lock Record关联(对象头的MarkWord中的Lock Word指向Lock Record的起始地址),同时Lock Record中有一个Owner字段存放拥有该锁的线 锁的升级、降级

  当竞争出现时,默认会使用偏向锁。JVM 会利用CAS 操作,在对象头的 mark word 部分设置线程ID ,以表示这个对象偏向于当前的线程,所以并不涉及到真正的互斥锁。 这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏向锁可以降低无竞争开销。如果有另外的线程试图锁定某个已经被偏向过的对象,JVM 就需要撤销(revoke)偏向锁,并切换到轻量级锁。 轻量级锁利用CAS 操作mark word 来试图获取锁,如果尝试成功,就使用普通的轻量级锁(锁标识位为00);否则,进一步升级为重量级锁。

  浅谈局域网操作系统的分类及功能.pdf 浅谈局域网操作系统的分类及功能.pdf 浅谈局域网操作系统的分类及功能.pdf

  总结:单例模式必然会被多个线程访问。多线程访问一定要注意锁的问题。参考博主之前分享的转载文章《java中的锁》,有几种方式如下:自旋锁类锁和对象锁共享锁和排它锁偏向锁互斥锁闭锁活锁分段锁无锁无状态编程...

  中的各种锁以及最优的zookeeper分布式锁解决方案04-16阅读数 447

  读写锁读写锁并没有分拆锁定粒度,而是区分操作的性质,读与读可以并发,读-写,写-写互斥,只有写是独占锁。分拆锁和分离锁的思想都是对于复杂数据机构,不同的部分对应不同的锁,降低锁的粒度分拆锁,split...博文来自:binling的专栏

  unsafe类给我们提供了很多很牛逼方法,他的操作都是原子性的,用来获取内存地址处的值并修修改,它的修改逻辑是对比内存里地址处的值是否和预期值一样(没有多线程修并发修改的情况下是一样的),如果不一样,...博文来自:

  阅读数 4335首先整理多线程同步的知识点,开头肯定是要先探讨探讨多线程同步的问题。那么嘛叫线程安全问题呢?答:我们知道Jvm虚拟机的设计中线程的执行是抢占式的,线程的执行时间是由底层系统决定的。所以就会有多个线程访...

  1,如何保证线程安全不在线程之间共享状态变量将状态变量修改成不可变的变量在访问状态变量时使用同步 2,实现线程安全有那些方法A:内置锁publicclasswidget{publicsynchroni...博文来自:sdywcd——jshoper3x开源商城系统作者

  1.LockSupport工具类LockSupport主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用Loc...

  volatile volatile类型变量是:CPU直接读写变量所在的内存,而不是把变量copy到寄存器操作这样对变量的操作所线程都是可见的这样做的结果是减少了并发时冲突的概率但不能完全避免,并不是原...

  当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先...

  在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级...

  (1)互斥锁一次只能一个线程拥有互斥锁,其他线程只有等待。互斥锁是在抢锁失败的情况下主动放弃CPU进入睡眠状态直到锁的状态改变时再唤醒,互斥锁在加锁操作时涉及上下文的切换。(2)自旋锁在任何时刻同样只...

  redis命令解释说道Redis的分布式锁都是通过setNx命令结合getset来实现的,在讲之前我们先了解下setNx和getset的意思,在redis官网是这样解释的注:redis的命令都是原子操...

  同步与异步,如何解决线程安全问题—synchronized详解,对象锁与类锁,静态与非静态同步方法详解

  授予每个自然月内发布4篇或4篇以上原创或翻译IT博文的用户。不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!

  授予每个自然周发布1篇到3篇原创IT博文的用户。本勋章将于次周周三上午根据用户上周的博文发布情况由系统自动颁发。

http://talkbirds.com/zhilingzhongshi/423.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有