首页 > 编程语言 >Java - 线程同步方式

Java - 线程同步方式

时间:2022-10-28 12:06:07浏览次数:45  
标签:同步 Java synchronized void 线程 自旋 public


概念

同步异步


消息通信机制


  • 同步:发出调用之后,在没有得到结果之前,该调用不返回。调用者主动等待调用结果。
  • 异步:发出调用之后,该调用直接返回,无结果。被调用者通过状态、通知进行反馈,或者通过回调函数处理这个调用。

阻塞非阻塞


调用者等待调用返回值时的状态


  • 阻塞:调用结果返回之前,当前线程[ 调用者]会被挂起
  • 非阻塞:不能立刻得到结果之前,调用者线程不会被阻塞


锁机制的意义

Java 允许多线程并发控制,当多个线程同时操作(增删改查)一个被共享的资源变量时,可能导致数据出现不正确的结果。加入锁保证了变量的唯一性,准确性。

Java 中的锁

公平锁、非公平锁


公平锁要求多个等待锁的线程,必须按照申请锁的顺序获得锁,而非公平锁允许抢占锁。公平锁的优点是稳定,等待锁的线程不会饿死;非公平锁的优点是性能优,因为某些线程可以避开Blocking,Runnable 之间的状态转换。


自旋锁


线程在进行状态转换(操作系统层面)过程要耗费很多时间,并且很多时候共享数据的锁定状态只会持续很短一段时间,为了节省这段很短的时间去挂起、恢复现场并不值得。如果机器上有两个以上的处理器,可以让两个线程并行执行,我们可以后面的线程“稍等片刻”,让他执行一个“自旋”。自旋等待不能代替阻塞。自旋占用处理器时间,如果等待时间长,还是要让线程阻塞。自旋只是在轻量级锁中使用,在重量级锁中,线程不能使用自旋。


锁消除


锁消除是虚拟机JIT在运行时,检测一些被同步的代码,不可能存在共享数据竞争的情况,锁将进行消除的操作。


锁粗化


锁范围扩展被称之为锁粗化。


可重入锁


可重入锁,也叫递归锁。线程A 持有一个锁,线程B 不持有锁,B 访问被这个锁保护的代码就会被阻塞,而A 则可以利用其持有的这个锁多次访问这段代码。Java 内置锁(synchronize)和Lock(ReentrantLock)都是可重入的。


类锁和对象锁


public class LockStrategy
{
public Object object1 = new Object();
// 类锁
public static synchronized void method1(){}
public void method2(){
synchronized(LockStrategy.class){}
}
// 对象锁
public synchronized void method4(){}
public void method5()
{
synchronized(this){}
}
public void method6()
{
synchronized(object1){}
}
}




例题,有一个类这样定义:

public class SynchronizedTest
{
public synchronized void method1(){}
public synchronized void method2(){}
public static synchronized void method3(){}
public static synchronized void method4(){}
}


那么,有SynchronizedTest的两个实例a和b,对于一下的几个选项有哪些能被一个以上的线程同时访问呢?


A. a.method1() vs. a.method2()


B. a.method1() vs. b.method1() // a,b,对象锁


C. a.method3() vs. b.method4()


D. a.method3() vs. b.method3()


E. a.method1() vs. a.method3() //对象锁,类锁


答案是什么呢?BE


偏向锁、轻量级锁和重量级锁


Java 对象内存布局中,对象头的Mark Word:



Java - 线程同步方式_公平锁



整个 synchronized 锁流程如下:

  1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁(偏向于第一个获得它的线程)
  2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1
  3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。
  4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁
  5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
  6. 如果自旋成功则依然处于轻量级状态。
  7. 如果自旋失败,则升级为重量级锁

悲观锁、乐观锁


悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
乐观锁:假定不会发生并发冲突,只在提交操作时检测是否违反数据完整性。(使用版本号或者时间戳来配合实现)


共享锁,排它锁


共享锁:读操作。事务A 对Data 加锁之后,事务B 依然可以对Data 加共享锁(排它锁不可以),读操作。


排它锁:读、修改操作。事务A 对Data 加锁之后,事务B 不可以对Data 加任何锁。


读写锁


资源能够被多个读线程访问,或者,只被一个写线程访问(不能存在读线程)。Java当中的读写锁通过ReentrantReadWriteLock实现。


互斥锁


所谓互斥锁就是指一次最多只能有一个线程持有的锁。synchronized和Lock就是互斥锁。





线程同步的方式有哪些?


  1. 同步方法:synchronized 关键字修饰的方法
  2. 同步代码块:synchronized 关键字修饰的语句块
  3. 使用特殊域变量(volatile)实现线程同步
  4. 使用reentrantLock 实现线程同步
  5. 使用局部变量实现线程同步:如果使用ThreadLocal 管理变量,则每一个使用该变量的线程都获得一个该变量的副本,副本之间相互独立,这样每个线程都可以修改自己的变量副本,而不会对其他线程产生影响。


标签:同步,Java,synchronized,void,线程,自旋,public
From: https://blog.51cto.com/u_11290086/5804045

相关文章

  • Java - 重载 与 重写 的区别
    静态分派与动态分派1.静态类型[外观类型]=>方法执行版本=>编译期确定2.实际类型=>方法执行版本=>运行期确定静态解析与动态连接1.静态解析:符号引用(c......
  • Java - 类加载机制
    加载=>验证、准备、解析(链接)=>初始化类初始化时机1. 遇到new,getstatic,putstatic,invokestatic这四条字节码指令时,如果类没有进行过初始化,需要进行初始化......
  • Java - synchronized 和 volatile
    1.两者均可以实现可见性(线程B可以读到线程A更新的最新值)2.volatile本质就是告诉JVM,当前变量在寄存器中的值不确定,需要从主存中读取。synchronized锁定了当前变量,只有......
  • Java - Java 中的四种引用
    强引用Objecto=newObject();只要强引用还存在,垃圾回收器就永远不会回收掉该对象。软引用描述有用但是非必需的对象,使用softReference类来实现是否回收对象取决于J......
  • Java - 垃圾回收算法与垃圾回收器
    垃圾回收算法1.标记-清除过程:标记所有需要回收的对象,标记结束后,回收所有被标记的对象缺点:效率低下,容易造成碎片2.复制将内存空间分为两部分,每次只使用一部分,当一部分用......
  • Java - 简单可达性分析
    可以作为GCRoot的对象1.方法区中常量引用的对象2.方法区中静态属性引用的对象3.虚拟机栈中引用的对象4.本地方法栈中引用的对象可达性分析通过GC......
  • Java - <clinit> 与 <init>
    1..java=>编译=>.class=><clinit>=>类构造器2.类构造器将静态变量(初始化)和静态语句块收敛(将一系列操作集合起来执行)到<clinit>收敛顺序:父类静态变量......
  • Java - 简述JVM 内存模型
    JVM[来源:维基百科]程序计数器程序计数器是当前线程执行的字节码的行号指示器;程序计数器线程私有;程序计数器是JVM规范中唯一一个没有任何OutOfMemoryError的区域;虚......
  • Java - 创建子类实例时要先创建父类实例
    1.会访问方法区中父类的元数据2.会在堆内存中为父类分配空间,用来存储父类实例属性3.但是,这块堆内存属于子类所有......
  • Java中利用循环向list中add添加元素得到的都是最后一个
     背景:通过增强for循环,list.add()赋值,发现得到的元素都是最后一个  原因:list在add添加元素时访问的是该对象的引用地址,list中存储的全是实例化后的对象的地址,而java......