Java Synchronized

2022-02-24  本文已影响0人  yikemi

一、为什么需要锁

存在共享数据。
当出现ConcurrentModificationException的时候,存在多个线程对一个集合同时进行遍历或者修改。
单线程就不需要考虑这种情况。

二、解决方案

当前类中涉及集合修改和遍历操作加上synchronized关键字,或者是这两个逻辑放在一个线程中完成。

1、可重入&&互斥

synchronized是可重入锁;ReentrantLock也是。
即同一个线程可以输出Hello World不会死锁。

    // 可重入
    public void syncsTask() {
        synchronized (this) {
            System.out.println("Hello");
            synchronized (this){
                System.out.println("World");
            }
        }
    }

synchronized是互斥锁,满足互斥性(操作的原子性),可见性。
synchronized锁的不是代码,是对象。
同步代码块synchronized(this)和同步方法锁的是同一个对象。
类锁和对象锁是互不干扰的。只有使用同一把锁线程之间才会干扰。
Java对象头中都存在一个Monitor对象,这也是Java中任意对象可以作为锁的原因。

2、为什么很多人对它嗤之以鼻

早前版本是重量级锁,依赖于系统的Mutex Lock(互斥),线程之间切换从用户态转换至核心态,开销大。
jdk6之后性能已经提升。

3、自旋锁与自适应自旋锁

jdk6默认开启,不挂起线程,但如果锁占用时间过长,就不再推荐使用了。
让线程处于忙循环等待锁释放,不出让CPU,减少线程的切换。

4、锁消除

JIT编译时,对运行上下文进行扫描,去除不可能存在竞争的锁。

5、锁粗化

JVM对锁的范围进行扩大,减少锁同步的代价。

6、synchronized的四个演变阶段

锁膨胀的方向:无锁、偏向锁、轻量级锁、重量级锁
偏向锁:CAS,指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价
轻量级锁:偏向锁升级而来,适用于线程交替执行同步块,自旋
重量级锁:同步块或者方法执行时间较长,追求吞吐量


对象头不同阶段的演变

三、synchronized和static synchronized区别

一个锁的是类对象,一个锁的是实例对象。
若类对象被lock,则类对象的所有同步方法(static synchronized 修饰)全被lock;
若实例对象被lock,则该实例对象的所有同步方法(synchronized 修饰)全被lock。
每个synchronized方法都必须获得调用该方法的类实例的”锁“方能执行,否则所属线程阻塞。

方法一旦执行,就会独占该锁,一直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,从而重新进入可执行状态。这种机制确保了同一时刻对于每一个类的实例,其所有声明为synchronized的成员函数中之多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突。

四、synchronized方法与synchronized代码块

    private synchronized void syncFunc() {
        // do something
    }

    private void syncBlockFunc() {
        synchronized (this) {
            // do something
        }
    }

synchronized methods() {}与synchronized(this){}之间没有什么区别,只是synchronized methods() {} 便于阅读理解,而synchronized(this){}可以更精确的控制冲突限制访问区域,有时候表现更高效率。

五、synchronized和ReentrantLock区别

所属不同关键字,类。
底层实现不同MarkWord,Unsafe类。
ReentrantLock可以选择公平(fair)锁(排队打饭)和非公平锁,构造函数传入true;
synchronized是非公平的(堵车时的加塞)。

// 一般通常会使用try catch finally方式
public class ReentrantLockDemo implements Runnable {
    private static ReentrantLock lock = new ReentrantLock(false);

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " get lock");
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            } finally {
                lock.unlock();
            }
        }
    }

将synchronized转换为直观可控的对象行为。

上一篇下一篇

猜你喜欢

热点阅读