Java虚拟机--锁

        锁能够保证某一块代码块在执行时候只能够有一个线程能够进入,我们常见的就是synchronized了,Java SE 1.6钱该锁是重量级锁(发生竞争关系时会主动进入睡眠状态),之后进行了各种的优化,该情况就没有那么重了。本文将详细介绍偏向锁以及轻量级锁以及锁的存储结构和升级过程。这些内容均来自于方腾飞《并发编程的艺术》以及本人做的二次加工。

    先来看看利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下三种形式:

   1)对于普通同步方法,锁是当前实例对象

   2)对于静态同步方法,锁是当前类的Class对象

   3)对于同步方法块,锁是Synchronized括号里配置的对象

    一个线程是图访问同步块是,必须先得到锁,退出或者抛出异常时必须释放锁。锁到底存在哪里呢?从JVM规范可以看出来,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。代码块同步使用monitorenter 和 monitorexit指令实现的,方法同步是另一种。方法同步可以使用这两个指令来实现。可以看看这个链接的例子:http://m635674608.iteye.com/blog/1748415

   锁存在哪里呢?由于每一个Java对象都有一个对象头,其中有个叫做Mark Word里面记录着锁的状态,有(轻量级锁,重量级锁,偏向锁以及GC标记)

    Java SE 1.6中,锁有4种状态,级别从低到高是:无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级,而且只能升级,不能降级。

锁的优缺点对比

优点缺点 适用场景
偏向锁加锁和解锁不需要额外的消耗,
和执行非同步方法相比仅存在纳秒级的差距
如果线程间存在锁竞争,
会带来额外的锁撤销的消耗
适用于只有一个线程访问同步
块场景
轻量级锁竞争的线程不会阻塞,提高了
程序的响应速度
如果始终得不到锁竞争的线程,
使用自旋会消耗CPU   
追求响应时间
同步块执行速度非常快
重量级锁线程竞争不使用自旋,不会消耗CPU   线程阻塞,响应时间慢追求吞吐量
同步块执行速度较长   

锁的内存语义

锁的释放-获取建立的happens-before关系

     锁是Java并发编程中个最重要的同步机制。锁除了让临界区户次执行外,还可以让释放锁的现场向获取同一个锁的线程发送消息。

class MonitorExample {
	int a = 0;
	
	public synchronized void writer() {     //1
		a ++;                           //2
	}                                       //3
	public synchronized void reader() {     //4
		int i = a;                      //5
	      ......                          
    }                                       //6
}

   假设A线程执行writer()方法,随后线程B执行reader()方法。根据happens-before规则,这个过程包含的happens-before关系可以分为3类。

    1)根据程序次序规则,1 happens-before 2, 2 happens-before 3; 4 happens-before 5, 5 happens-before 6 。

    2)根据监视器锁规则,3 happens-before 4。

    3)根据happens-before 的传递性, 2 happens-before 5。

锁的释放和获取的内存语义


锁内存语义的实现

借助ReentrantLock源代码来分析锁内存语义的具体实现机制

class ReentrantLockExample {
	int a = 0;
	ReentrantLock lock = new ReentrantLock();
	
	public void writer() {
		lock.lock();
		try {
			a++;
		} finally {
			lock.unlock();
		}
	}
	
	public void reader() {
		lock.lock();
		try {
			int i = a;	
		} finally {
			lock.unlock();
		}
		
	}
}
在ReentrantLock中,调用lock()方法获取锁,调用unlock()方法解锁。

使用公平锁,加锁方法lock()调用轨迹如下。

1)ReentrantLock:lock()

2)FairSync:lock

        final void lock() {
            acquire(1);
        }

3)AbstractQueuedSynchronized: acquire(int arg)。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
4)ReentrantLock:tryAcquire(int acquires)。


        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

在这里,state是volatile,我们能够看到加锁首先获取state变量。

在使用公平锁时,解锁方法unlock()调用轨迹如下。

1)ReentrantLock:unlock()

2)AbstractQueuedSynchronizer:release(int arg)

3)Sync:tryRelease(int releases)

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值