synchronized关键字

 什么是synchronized关键字?

synchronized 是 Java 编程语言中的一个关键字,它提供了一种内置的锁机制来支持线程间的同步,从而防止多个线程同时访问某个类的实例变量或静态变量。当某个线程进入由 synchronized 保护的代码块或方法时,它会获得相应的锁,其他线程则无法同时进入该代码块或方法,直到锁被释放。

 java内存的可见性问题

在了解synchronized关键字的底层原理之前,需要了解java的内存模型,看看synchronized关键字如何起作用的。

这里的本地内存并不是真实存在的,只是Java内存模型的一个抽象概念,它包含了控制器、运算器、缓 存等。同时Java内存模型规定,线程对共享变量的操作必须在自己的本地内存中进行,不能直接在主内 存中操作共享变量。这种内存模型会出现什么问题呢?

1. 线程A获取到共享变量X的值,此时本地内存A中没有X的值,所以加载主内存中的X值并缓存到本地 内存A中,线程A修改X的值为1,并将X的值刷新到主内存中,这时主内存及本地内存中的X的值都 为1。

2. 线程B需要获取共享变量X的值,此时本地内存B中没有X的值,加载主内存中的X值并缓存到本地内 存B中,此时X的值为1。线程B修改X的值为2,并刷新到主内存中,此时主内存及本地内存B中的X 值为2,本地内存A中的X值为1。

3. 线程A再次获取共享变量X的值,此时本地内存中存在X的值,所以直接从本地内存中A获取到了X为 1的值,但此时主内存中X的值为2,到此出现了所谓内存不可见的问题。

该问题Java内存模型是通过synchronized关键字和volatile关键字就可以解决,那么synchronized关键 字是如何解决的呢,其实进入synchronized块就是把在synchronized块内使用到的变量从线程的本地内 存中擦除,这样在synchronized块中再次使用到该变量就不能从本地内存中获取了,需要从主内存中获 取,解决了内存不可见问题。

 synchronized的三大特性是什么?

  1. 可见性(Visibility):
    当一个线程进入一个被synchronized保护的代码块或方法时,它首先会获得监视器锁(monitor lock)。在获得锁的过程中,Java内存模型确保线程能够看到一个一致性的共享变量视图。这意味着,当一个线程释放锁时,它会将共享变量的修改刷新到主内存中,因此其他线程在随后获得锁时,能够看到一个已经被更新过的变量值。这保证了synchronized块或方法内的共享变量修改的可见性。

  2. 原子性(Atomicity):
    synchronized块或方法内的代码是原子的,即一旦一个线程进入了synchronized保护的区域,它就可以不受干扰地执行完该区域的代码。其他线程在此期间无法进入这个区域,直到当前线程释放锁。因此,对于synchronized保护的代码块或方法,其内部的操作是原子的,不会被其他线程中断。这确保了代码执行的完整性,避免了原子性问题。

  3. 有序性(Ordering):
    synchronized还提供了一定程度的“Happens-Before”关系,这意味着在synchronized块或方法内部的所有操作都必须在释放锁之前完成,并且这些操作对于随后获得同一个锁的线程是可见的。这确保了即使在Java内存模型允许指令重排序的情况下,synchronized块或方法内部的代码执行顺序对于其他线程来说仍然是有序的。

 synchronized可实现什么类型的锁?

  1. 悲观锁:synchronized实现的是一种悲观锁策略。悲观锁在数据处理过程中总是假设最坏的情况,即认为数据会在处理过程中被其他线程修改,因此每次访问共享资源时都会进行加锁操作,以确保数据的一致性。
  2. 非公平锁:synchronized默认实现的是非公平锁。非公平锁的特点在于,当多个线程尝试获取同一个锁时,并不会按照它们请求锁的顺序来分配锁,而是允许等待的线程以随机的方式获取锁。这种机制可能会产生线程饥饿现象,但可以提高系统的整体吞吐量。
  3. 可重入锁:synchronized也是可重入锁的实现。可重入锁的特点是,一个已经拥有锁的线程可以再次获取这个锁,而不会产生死锁。在Java中,如果一个线程进入了一个由synchronized保护的同步块或方法,并且这个线程已经持有这个锁,那么它可以无限制地重新进入这个同步块或方法,而无需再次获取锁。
  4. 独占锁或排他锁:synchronized实现的锁是独占锁,这意味着在任何时候,只有一个线程可以持有这个锁。当一个线程持有锁时,其他试图进入同步块或方法的线程都会被阻塞,直到锁被释放。

 synchronized关键字的使用方式

synchronized主要有三种使用方式:修饰普通同步方法、修饰静态同步方法、修饰同步方法块。

1. 修饰普通同步方法

public class SynchronizedInstanceMethod {  
    public synchronized void ordinarySynchronizedMethod() {  
        // 同步代码块,同一时间只有一个线程可以执行此方法  
        System.out.println("Ordinary synchronized method executed by thread: " + Thread.currentThread().getName());  
    }  
  
    public static void main(String[] args) {  
        SynchronizedInstanceMethod obj = new SynchronizedInstanceMethod();  
        new Thread(() -> obj.ordinarySynchronizedMethod(), "Thread-1").start();  
        new Thread(() -> obj.ordinarySynchronizedMethod(), "Thread-2").start();  
    }  
}

2. 修饰静态同步方法

public class SynchronizedStaticMethod {  
    public static synchronized void staticSynchronizedMethod() {  
        // 同步代码块,同一时间只有一个线程可以执行此方法  
        System.out.println("Static synchronized method executed by thread: " + Thread.currentThread().getName());  
    }  
  
    public static void main(String[] args) {  
        new Thread(() -> SynchronizedStaticMethod.staticSynchronizedMethod(), "Thread-1").start();  
        new Thread(() -> SynchronizedStaticMethod.staticSynchronizedMethod(), "Thread-2").start();  
    }  
}

3. 修饰同步方法块

public class SynchronizedBlock {  
    private final Object lock = new Object();  
  
    public void synchronizedBlockMethod() {  
        // 同步代码块,同一时间只有一个线程可以执行这个代码块  
        synchronized (lock) {  
            System.out.println("Synchronized block executed by thread: " + Thread.currentThread().getName());  
        }  
    }  
  
    public static void main(String[] args) {  
        SynchronizedBlock obj = new SynchronizedBlock();  
        new Thread(() -> obj.synchronizedBlockMethod(), "Thread-1").start();  
        new Thread(() -> obj.synchronizedBlockMethod(), "Thread-2").start();  
    }  
}

在以上三个示例中:

  • 第一个示例中,ordinarySynchronizedMethod方法被synchronized修饰,它对整个方法体进行同步,保证了同一时间只有一个线程能够访问这个方法。
  • 第二个示例中,staticSynchronizedMethod是一个静态同步方法,它对整个静态方法体进行同步,保证了同一时间只有一个线程能够访问这个类的静态同步方法。
  • 第三个示例中,synchronizedBlockMethod方法中有一个同步代码块,它使用synchronized(lock)对特定的lock对象进行同步,保证了同一时间只有一个线程能够执行这个同步代码块。这里使用了一个私有的Object类型的lock变量作为锁对象,而不是直接同步在方法或类上,这提供了更大的灵活性。

 

 

 

  • 31
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值