【JAVA书单】-《JAVA多线程核心技术》-第四章 Lock的使用

7 篇文章 0 订阅

此文是对《JAVA多线程编程核心技术》的一点总结,如果想要了解具体细节可以去看原书。

第四章 Lock的使用

lock和synchronized的区别

  • 用法:
    synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。

    lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。

  • 性能:
    synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。

  • 用途
    synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock

公平锁与非公平锁:

公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。从这个角度讲,synchronized其实就是一种非公平锁。

ReentrantLock类的使用

在 Java 多线程中, 可以使用 synchronized 关键字来实现多线程之间同步互斥, 但在 JDK 1.5 中新增加了 ReentrantLock 类也能达到同样的效果, 并且在扩展功能上也更加强大, 比如具有嗅探锁定, 多路分支通知, 公平锁和非公平锁等(默认)功能, 而且在使用上也比 synchronized 更加的灵活

public class MyService {

    private Lock lock = new ReentrantLock();

    public void testMethod() {
    try{
    		 lock.lock();
        	for (int i = 0; i < 10; i++){
            	System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1)));
        	}
    	)catch (InterruptedException e) {
           e.printStackTrace();
       } finally {
       		lock.unlock();
       }
    }
}
public class MyThread extends Thread {

    private MyService myService;
    public MyThread(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.testMethod();
    }
}

public static void main(String[] args) throws IOException, InterruptedException {

        MyService myService = new MyService();

        MyThread myThreadA = new MyThread(myService);
        MyThread myThreadB = new MyThread(myService);
        MyThread myThreadC = new MyThread(myService);
        MyThread myThreadD = new MyThread(myService);
        MyThread myThreadE = new MyThread(myService);

        myThreadA.start();
        myThreadB.start();
        myThreadC.start();
        myThreadD.start();
        myThreadE.start();

    }

从运行结果来看, 当前线程打印完毕之后将锁进行释放, 其他的线程才可以继续打印. 线程打印的数据是分组打印, 因为当前线程已经持有锁, 但线程之间打印的顺序是随机的.

使用 Condition 实现等待/通知

关键字 synchronized 与 wait() 和 notify() / notifyall() 方法结合可以实现等待/通知模式, 只不过在使用时, 调用 notify() 方法 JVM 会随机选择一个 WAITNG 状态的线程来执行.

而使用 Condition 则可以更加灵活, 可以实现 “选择性通知”, 可以指定的选择唤醒哪些线程, 哪些线程继续等待.

public class MyService {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await() {
        try {
            lock.lock();
            System.out.println(" await时间为" + System.currentTimeMillis());
            condition.await();
        }catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void signal() {
        try {
            lock.lock();
            System.out.println("sigal时间为:" + System.currentTimeMillis());
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}
public class ZmsThread extends Thread{
    private MyService service;

    public ZmsThread(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.await();
    }
}
public class ZmsRun {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        ZmsThread a = new ZmsThread(myService);
        a.start();
        Thread.sleep(3000);
        myService.signal();
    }
}
Object类与Condition类
  • Object类的wait方法相当于Condition类的await方法
  • Object类的notify方法相当于Condition类的signal方法
  • Object类的notifyAll方法相当于Condition类的signalAll方法
常用方法

int getHoldCount() 查询调用 lock() 方法的次数.

final int getQueueLength() 估计等待锁的线程数. 比如有5个线程, 1个线程首先执行 await() 方法, 那么在调用此方法后返回值是4, 说明有4个线程同时在等待lock的释放.

int getWaitQueueLength(Condition condition) 返回与此锁相关联给定条件等待的线程数的估计. 比如有5个线程, 每个线程都执行了同一个 condition 对象的 await() 方法, 则调用此方法时返回的值是5.

final boolean hasQueuedThreads() 判断是否有线程等待此锁.

final boolean hasQueuedThread(Thread thread) 判断指定线程是否等待获取此锁.

boolean hasWaiters(Condition condition) 判断线程有没有调用 await() 方法.

void lockInterruptibly() throws InterruptedException 获取锁, 除非当前线程为interrupted

ReentrantReadWriteLock类

类ReentrantLock具有完全互斥排他的效果,这样做虽然保持了实例变量的线程安全性,但效率是非常低下的。所以JDK中提供了一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在不需要操作实例变量的方法中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值