juc之Lock的学习

Lock接口的主要方法

//尝试获取锁,超时就放弃当前锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

//相当于把超时时间设置为无限,在等待的过程中,线程可以被打断
void lockInterruptibly() throws InterruptedException;

//注意不遵守公平原则,会获取到锁不管是否会有其它线程等待
boolean tryLock();



//最佳实践
if (lock.tryLock()) {
        try {
          // manipulate protected state
        } finally {
          lock.unlock();
        }
     } 

可见性保证

happens-befor原则:在一个线程解锁以后,另一个线程加锁时可以看见之前解锁线程的操作。

锁的分类

公平锁和非公平锁

定义:公平指的是按照线程的请求顺序,来分配锁。非公平指的是,不完全按照线程的请求顺序,在一定情况下可以插队。

为什么有非公平锁?

避免唤醒的空闲时间。

代码示例:

package lock.reentrantlock;

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 描述:     演示公平和不公平两种情况
 */
public class FairLock {

    public static void main(String[] args) {
        PrintQueue printQueue = new PrintQueue();
        Thread thread[] = new Thread[10];
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(new Job(printQueue));
        }
        for (int i = 0; i < 10; i++) {
            thread[i].start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Job implements Runnable {

    PrintQueue printQueue;

    public Job(PrintQueue printQueue) {
        this.printQueue = printQueue;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始打印");
        printQueue.printJob(new Object());
        System.out.println(Thread.currentThread().getName() + "打印完毕");
    }
}

class PrintQueue {

    private Lock queueLock = new ReentrantLock(true);

    public void printJob(Object document) {
        queueLock.lock();
        try {
            int duration = new Random().nextInt(10) + 1;
            System.out.println(Thread.currentThread().getName() + "正在打印,需要" + duration);
            Thread.sleep(duration * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }

        queueLock.lock();
        try {
            int duration = new Random().nextInt(10) + 1;
            System.out.println(Thread.currentThread().getName() + "正在打印,需要" + duration+"秒");
            Thread.sleep(duration * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }
    }
}

共享锁(读锁)和排它锁 

读写锁的规则:

要么是多个或者一个线程持有读锁,要么是一个线程有写锁,二者不会同时出现。

代码示例:

package lock.readwrite;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 描述:     TODO
 */
public class CinemaReadWrite {

    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

    private static void read() {
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "得到了读锁,正在读取");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "释放读锁");
            readLock.unlock();
        }
    }

    private static void write() {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "得到了写锁,正在写入");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "释放写锁");
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(()->read(),"Thread1").start();
        new Thread(()->read(),"Thread2").start();
        new Thread(()->write(),"Thread3").start();
        new Thread(()->write(),"Thread4").start();
    }
}

读写锁插队策略

  • 公平锁:不允许插队
  • 非公平锁:写锁可以随时插队,读锁仅在等待队列的头节点,不是想获取写锁的线程时可以插队

锁的升降级:ReentrantReadWriteLock支持锁的降级,获取写锁的同时可以获取读锁。

自旋锁和阻塞锁

自旋锁的定义:如果物理机有多个cpu,能够让两个或两个以上的线程并行执行代码,我们就可以让后面那个请求的线程不放弃cpu的执行时间,看看持有锁的线程是否很快释放锁。而为了让线程稍等一下,我们需要让当前线程进行自旋。如果在自旋完成后前面锁定的同步资源线程已经释放了锁,那么当前线程就可以不必阻塞而是直接获得同步资源。从而避免切换线程的开销。

阻塞锁的定义:如果线程没有拿到锁,会直接把线程阻塞,知道被唤醒

自旋锁的缺点:自旋时间长消耗cpu。

锁优化

jvm对锁的优化:自旋锁和自适应,锁销除

代码层面优化:缩小同步代码块,不要锁住方法,减少锁的次数

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章