码迷,mamicode.com
首页 > 编程语言 > 详细

java-多线程深入(六)锁

时间:2015-08-19 08:13:50      阅读:138      评论:0      收藏:0      [点我收藏+]

标签:java   多线程   

java多线程中提供的锁:synchronized和lock。

(一)synchronized

1、synchronized的使用

每个对象都自带锁,锁可以同步实例方法(this是对象锁)、静态方法(class是对象锁)、方法块(synchronized参数是对象锁

下面是锁住实例方法:

public synchronized void add(){
        a++;
    }
使用注意点:

(1)Object的wait、notify和notifyAll使用时需在代码外层加锁,等待和唤醒锁必须相同,使用的锁不能发生改变,不然会抛出IllegalMonitorStateException异常

/**
 * 线程等待唤醒测试
 * 
 * @author peter_wang
 * @create-time 2014-10-9 下午2:50:36
 */
public class ThreadNotifyTest {
    private static Integer num = new Integer(0);

    /**
     * @param args
     */
    public static void main(String[] args) {
        final Thread thead1 = new Thread() {
            @Override
            public void run() {
                synchronized (num) {
                    try {
                        sleep(2000);
                        num.wait();
                        System.out.println("解锁成功");
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thead1.start();

        Thread thead2 = new Thread() {
            @Override
            public void run() {
                try {
                    sleep(1000);
// num = new Integer(1);  //A
                    num++;//B
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        thead2.start();
    }
}
无论执行A或B,改变了锁num后,wait执行的时候会抛出IllegalMonitorStateException异常。
对wait、notify加锁是为了保证它们在执行中的原子性。

(2)使用的锁尽量是不可变对象,使用private final Object对象,可变化的对象可能导致不可预知的后果,如wait的问题。

(3)synchronized锁住区域尽量减少,提高性能。

2、synchronized原理探究

(1)线程状态

当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态用来区分请求的线程: 
Contention List:所有请求锁的线程将被首先放置到该竞争队列 
Entry List:Contention List中那些有资格成为候选人的线程被移到Entry List,降低对Contention List的争用

Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set 

OnDeck:任何时刻最多只能有一个线程正在竞争锁,该线程称为OnDeck Owner:获得锁的线程称为Owner !Owner:释放锁的线程
(2)锁类型

公平锁和非公平锁

公平锁:每个线程取得调度的几率是一样的

非公平锁:每个线程取得的调度几率不同,是公平锁吞吐率的5-10倍

自旋锁和阻塞锁

自旋锁:线程阻塞调度过程设计到操作linux内核线程,需要在用户态和内核态之间切换状态,性能消耗比较大,自旋机制让请求调度的线程内部自循环,不切换状态等待一段时间,若仍然未能获取调度机会再转换锁类型

阻塞锁:阻塞锁在线程竞争时,无获取到调度的线程直接进入阻塞队列

多种阻塞锁类型

偏向锁:在大多数情况下,锁都存在于单线程中,为了让线程获得锁减少性能代价,同一线程多次重入,不会执行CAS操作,直到遇见多线程竞争,转换成其他类型

轻量锁:偏向锁的升级版或者直接设置系统不使用偏向锁直接进入轻量锁,比偏向锁多了步CAS操作,当前若锁未被其他线程锁住即可使用,操作失败进入自旋锁

重量锁:完整的阻塞锁状态,对象监视器(Monitor),由自旋锁超时升级而成

锁的进化过程:偏向锁—>轻量锁—>自旋锁—>重量锁

(3)总结

synchronized执行时,优先使用偏向锁或轻量锁提升性能,碰到多线程锁住现象,进入自旋状态,等待未果进入重量锁阶段,阻塞线程,放入阻塞队列,切换线程状态。


(二)Lock

1、ReentrantLock的使用

private ReentrantLock mlock = new ReentrantLock();
@Override
    public void write() {
        mlock.lock();
        try {
            long startTime = System.currentTimeMillis();
            System.out.println("开始往这个buff写入数据…");
            for (;;)// 模拟要处理很长时间
            {
                if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
                    break;
            }
            System.out.println("终于写完了");
        }
        finally {
            mlock.unlock();
        }
    }
ReentrantLock在lock的时候锁住实例对象,必须在finally中unlock解锁,防止异常抛出未解锁。

2、ReentrantLock原理分析

ReentrantLock中的操作都是基于Sync,Sync继承自AbstractQueuedSynchronizer。

AbstractQueuedSynchronizer通过构造一个基于阻塞的CLH队列容纳所有的阻塞线程,而对该队列的操作均通过Lock-Free(CAS)操作,但对已经获得锁的线程而言,ReentrantLock实现了偏向锁的功能。


(三)synchronized和ReentrantLock对比

1、性能上synchronized是native方法性能优化较多,ReentrantLock是java层实现性能不一定很好。

2、ReentrantLock提供了更多功能,如公平锁和非公平锁设置等,但是需要使用finally解锁。

3、在普通情况下使用synchronized,在业务复杂需要使用ReentrantLock特殊功能的才使用ReentrantLock。


版权声明:本文为博主原创文章,未经博主允许不得转载。

java-多线程深入(六)锁

标签:java   多线程   

原文地址:http://blog.csdn.net/wangpeifeng669/article/details/42779591

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!