java中的一些锁

公平锁

公平锁就是按照有序的顺序,去依次获取锁,不允许插队。

非公平锁

非公平锁就是所有线程去抢锁,谁抢到谁先用,抢不到在去排队。

    // 默认是非公平锁
    Lock lock1 = new ReentrantLock();
    // 公平锁
    Lock lock2 = new ReentrantLock(true);

非公平锁源码如下:

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

公平锁源码如下:

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

可重入锁(递归锁)

可重入锁也叫递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一线程在外层方法获取锁的时候,在进入内层方法会自动的获取锁。
也就是说,线程可以进入任何一个已经拥有了锁的,所同步的代码块。
可重入锁的最大作用是避免死锁
ReentrantLock 和 synchronized 是可重入的非公平锁

自旋锁

自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,循环比较获取直到成功为止,没有类似wait的阻塞,缺点是循环会消耗CPU。
CAS中的Unsafe类中的自旋锁例子:

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

自旋锁Demo:

public class SpinLockDemo {
    // 原子引用线程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t invoked myLock()");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");
    }

    public static void main(String[] args) throws InterruptedException {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.myLock();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnlock();
        }, "t1").start();
        Thread.sleep(1000);
        new Thread(() -> {
            spinLockDemo.myLock();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnlock();
        }, "t2").start();
    }
}

独占锁

独占锁指该锁一次只能被一个线程所持有,对ReentrantLock 和 synchronized 而言都是独占锁。

读写锁

对ReentrantReadWriteLock,其写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的,读读,读写,写写的过程是互斥的。
多个线程同时读一个资源没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行,但是,如果有一个线程想去写共享资源,就不应该再有其他线程可以对该资源进行读或写。
写操作: 原子+独占,整个过程必须是一个完整的统一体,中间不允许被分割,被打断。

  • 读--读 --> 能共存
  • 读--写 --> 不能共存
  • 写--写 --> 不能共存

读写锁代码示例:

/**
 * 资源类
 */
class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
//    private Lock lock = new ReentrantLock();

    public void put(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在写入: " + key);
            try { Thread.sleep(300);}catch(InterruptedException e){e.printStackTrace();}
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 写入完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public void get(String key) {
        rwLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在读取");
            try {Thread.sleep(300);}catch(InterruptedException e){e.printStackTrace();}
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 读取完成: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public void clearMap() {
        map.clear();
    }

}

/**
 * 多个线程同时读一个资源没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行,
 * 但是,如果有一个线程想去写共享资源,就不应该再有其他线程可以对该资源进行读或写。
 * <p>
 * 读--读 --> 能共存
 * 读--写 --> 不能共存
 * 写--写 --> 不能共存
 * <p>
 * 写操作: 原子+独占,整个过程必须是一个完整的统一体,中间不允许被分割,被打断。
 *
 * @Classname ReadWriteLockDemo
 * @Date 2019/8/1 16:57
 * @Created by jianjieming
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 0; i < 5; i++) {
            final int tempInt = i;
            new Thread(() -> myCache.put(tempInt + "", tempInt + ""), String.valueOf(i)).start();
        }

        for (int i = 0; i < 5; i++) {
            final int tempInt = i;
            new Thread(() -> myCache.get(tempInt + ""), String.valueOf(i)).start();
        }

    }
}