Java 锁(可重入锁,自旋锁,读写锁)


本文主要介绍Java的一些锁:公平锁,非公平锁,可重入锁,自旋锁,以及它们的使用案例

公平锁与非公平锁

  1. 公平锁就是多线程按照申请锁的顺序来获取锁,先来后到。
  2. 非公平锁是多线程获取锁的顺序不是按照申请锁的顺序。可能造成优先级反转和饥饿现象,如果能抢占就抢占,否则就按照公平锁进行处理。

ReentrantLock默认的时候是非公平锁,但是也可以设置为公平锁。synchronized也是非公平锁。

可重入锁

可重入锁就是外层函数获取锁之后,内层代码仍然可以获取该锁的代码,在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。线程可以进入任何一个它所拥有锁的同步代码块。
从下列代码可以看出,线程A获取锁,进入到 increment() 方法,然后调用reduce方法,reduce方法也加了锁,但是可以直接获取到锁。不需要重新申请。

// synchronized
class Cat {
    private volatile int num = 0;

    public synchronized void increment() {
        num ++;
        System.out.println(Thread.currentThread().getName() + " " + num);
        reduce();
    }
    public synchronized void reduce() {
        num--;
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}
public class ReDemo {
    public static void main(String[] args) {
        Cat c = new Cat();
        new Thread(() -> {
            c.increment();
        },"A").start();

        new Thread(() -> {
            c.increment();
        },"B").start();
    }
}

lock 版本:

class Cat {
    private volatile int num = 0;
    Lock lock = new ReentrantLock();
    public void increment() {
        lock.lock();
        try {
            num ++;
            System.out.println(Thread.currentThread().getName() + " " + num);
            reduce();
        }catch(Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void reduce() {
        lock.lock();
        try {
            num--;
            System.out.println(Thread.currentThread().getName() + " " + num);
        }catch(Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public class ReDemo {
    public static void main(String[] args) {
        Cat c = new Cat();
        new Thread(() -> {
            c.increment();
        },"A").start();
        new Thread(() -> {
            c.increment();
        },"B").start();
    }
}

自旋锁

说到自旋锁,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;
    }

手动实现自旋锁:

public class SprinLock {
    AtomicReference<Thread> reference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println("尝试获取锁" + thread.getName());
        while (!reference.compareAndSet(null, thread)) {
        }
        System.out.println("获取到锁" + thread.getName());
    }
    public void myUnLock() {
        Thread thread = Thread.currentThread();
        reference.compareAndSet(thread, null);
        System.out.println("解锁" + thread.getName());
    }

    public static void main(String[] args) {
        SprinLock sprinLock = new SprinLock();
        new Thread(() -> {
            sprinLock.myLock();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sprinLock.myUnLock();
        }, "A").start();
        new Thread(() -> {
            sprinLock.myLock();
            sprinLock.myUnLock();
        }, "B").start();
    }
}

读写锁

读写锁就是再多线程的条件下,读读不加锁,读写和写读和写写都需要加锁。
读写锁demo:

class MyData {
    Map<String, Object> map = new HashMap<>();
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void put(String key, Object object) {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在写入线程" + key);
            Thread.sleep(300);
            map.put(key, object);
            System.out.println(Thread.currentThread().getName() + " 写入完成" + key);
        }catch(Exception e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }
    public void get(String key) {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在读取线程" + key);
            Thread.sleep(300);
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 读取完成" + result);
        }catch(Exception e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }
}
public class RreadOnWriteLockDemo {
    public static void main(String[] args) {
        MyData data = new MyData();
        for(int i=0; i<5; i++) {
            final int tmp = i;
            new Thread(() ->{
                data.put(tmp +"", tmp);
            },String.valueOf(i)).start();
        }
        for(int i=0; i<5; i++) {
            final int tmp = i;
            new Thread(() ->{
                data.get(tmp + "");
            },String.valueOf(i)).start();
        }
    }
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值