为什么Synchronized 是非公平锁?

1、什么是公平锁和非公平锁

公平锁和非公平锁是指在多线程环境下,如何对锁进行获取的顺序和策略的不同。

公平锁是指多个线程按照申请锁的顺序来获取锁,即先到先得的策略。当一个线程释放锁之后,等待时间最长的线程将获得锁。公平锁的优点是保证了每个线程的公平性,不存在饥饿现象,但是由于需要维护一个等待队列,因此会增加系统的开销。

非公平锁是指多个线程获取锁的顺序是不确定的,不一定按照申请锁的顺序来获取锁。当一个线程释放锁之后,锁的获取是由系统的调度算法来决定的。非公平锁的优点是可以减少线程上下文切换的开销,提高系统的吞吐量,但是容易出现饥饿现象,即某些线程可能会一直获取不到锁。

一般来说,公平锁适用于对线程公平性要求比较高的场景,而非公平锁适用于对性能要求比较高的场景。但是在具体应用时,需要根据实际情况来选择合适的锁。

2、Java中的公平锁和非公平锁

Java中的公平锁和非公平锁主要有以下几种:

  1. ReentrantLock的公平锁和非公平锁:ReentrantLock是Java中常用的锁实现类之一,它提供了公平锁和非公平锁两种模式。在创建ReentrantLock对象时,可以通过构造函数传入一个布尔类型的fair参数来指定锁的模式。如果fair为true,则创建公平锁;如果fair为false,则创建非公平锁。
  2. ReentrantReadWriteLock的公平锁和非公平锁:ReentrantReadWriteLock是一个读写锁,它也提供了公平锁和非公平锁两种模式。在创建ReentrantReadWriteLock对象时,可以通过构造函数传入一个布尔类型的fair参数来指定锁的模式。如果fair为true,则创建公平锁;如果fair为false,则创建非公平锁。
  3. StampedLock的乐观锁和悲观锁:StampedLock是Java 8中新增的一种锁实现类,它提供了乐观锁和悲观锁两种模式。在使用StampedLock时,可以通过调用tryOptimisticRead()方法来获取乐观锁,或者通过调用readLock()方法来获取悲观锁。乐观锁是一种无锁的机制,它不会阻塞线程,但是需要通过validate()方法来检查锁是否仍然有效。
  4. Synchronized的非公平锁:Synchronized是Java中内置的锁机制,它默认采用非公平锁模式。在使用Synchronized时,如果多个线程同时请求锁,则会根据系统的调度算法来决定哪个线程获取锁。

公平锁和非公平锁的性能和效率都会受到多种因素的影响,如锁的争用情况、线程的数量、系统的负载等。在实际应用中,需要根据具体情况选择合适的锁模式。

3、为什么Synchronized是非公平锁

Synchronized是Java中内置的锁机制,它的锁模式是非公平锁,这是因为Synchronized的实现方式是基于对象监视器(monitor)的。

在Java中,每个对象都有一个与之关联的monitor,当一个线程需要获取某个对象的锁时,它会首先尝试获取这个对象关联的monitor。如果monitor已经被其他线程占用,那么这个线程就会进入monitor的等待队列中,等待其他线程释放锁。

在Synchronized中,当一个线程释放锁时,JVM会从等待队列中随机选择一个线程来获取锁,而不是按照申请锁的顺序来获取锁,因此Synchronized是一种非公平锁。这种实现方式的优点是可以减少线程上下文切换的开销,提高系统的吞吐量,但是容易出现饥饿现象,即某些线程可能会一直获取不到锁。

Synchronized也可以通过在代码中使用wait()和notify()方法来实现公平锁,但是这种方式比较复杂,容易出错,而且性能也不如ReentrantLock的公平锁实现方式。因此,在实际应用中,如果需要公平锁,建议使用ReentrantLock。

4、Synchronized代码示例

下面是一个简单的Synchronized使用示例:

4.1、修饰方式

ini复制代码public class SynchronizedExample {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }
    public static void main(String[] args) throws InterruptedException {
        SynchronizedExample example = new SynchronizedExample();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println("Count: " + example.count);
    }
}

输入结果如下:

编辑切换为居中

添加图片注释,不超过 140 字(可选)

在这个示例中,Synchronized被用于修饰increment()方法,这样在同一时刻只有一个线程可以执行该方法。这个示例中创建了两个线程,分别对count变量进行自增操作。由于Synchronized的作用,这些操作是互斥的,所以最终输出的count值应该是20000。

4.2、修饰代码块

除了修饰方法,Synchronized还可以修饰代码块。下面是一个使用Synchronized修饰代码块的示例:

ini复制代码public class SynchronizedExample {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedExample example = new SynchronizedExample();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                example.increment();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println("Count: " + example.count);
    }
}

输出结果如下:

编辑切换为居中

添加图片注释,不超过 140 字(可选)

在这个示例中,Synchronized被用于修饰代码块,这样在同一时刻只有一个线程可以执行该代码块。在这个示例中,lock对象被用于作为Synchronized锁的对象。

5、总结

需要注意的是,Synchronized锁的是对象,而不是代码。在同一个对象上使用Synchronized修饰的代码块或方法是互斥的,但不同对象上的Synchronized代码块或方法并不互斥。

Synchronized虽然可以保证线程安全,但是它会导致性能问题。在使用Synchronized时需要注意以下几点:

  1. 尽量缩小Synchronized的作用范围,只在必要的代码块或方法上使用Synchronized。
  2. 尽量不要在Synchronized代码块或方法中调用其他耗时的方法,这会导致其他线程长时间等待。
  3. 尽量使用Lock接口代替Synchronized,因为Lock接口提供了更灵活的锁定机制。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
synchronized 是一种内置的机制,用于实现线程之间的同步。它是非公平的一种实现方式,这意味着在多个线程竞争同一个时,没有明确的顺序保证,无法保证先来后到的顺序。 synchronized 是非公平的原因有以下几点: 1. 性能考虑:公平相较于公平的开销更小。在公平中,每个线程在等待时会按照先后顺序排队,需要维护一个有序队列。而公平则没有维护这个队列的开销,所以在高并发情况下,公平具有更高的吞吐量。 2. 竞争环境下的效率:在多个线程竞争同一个的情况下,公平允许新来的线程插队获取,减少了线程切换的开销。如果当前持有的线程释放后,正好有一个新的线程在等待获取,那么这个新的线程就可以立即获取到,而不需要通过竞争和等待其他线程释放。 然而,公平也存在一些问题: 1. 不公平性:公平可能导致某些线程长时间处于等待状态,而其他线程反复获取,造成线程饥饿现象。 2. 可能产生优先级倒置:如果高优先级的线程在一直等待低优先级的线程释放时,可能会导致优先级倒置的问题,即高优先级的线程无法获得,降低了系统的响应性能。 相关问题: 1. 什么是公平?请举例说明。 2. synchronized 关键字有哪些使用方法? 3. Java 中除了 synchronized 之外,还有哪些线程同步机制?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值