JAVA中锁的介绍

公平锁和非公平锁

公平锁:

公平锁,就是很公平,在并发环境中每个线程在获取锁时首先会查看此锁维护的等待队列,如果是空说明自己是第一个就会获得到锁,如果不为空就会加入到队列中等待排队。

非公平锁:

非公平锁上来就尝试占有锁,如果尝试失败,才会加入到队列中。

多线程获取锁并不是按照申请锁顺序,有可能后申请锁的线程比先申请锁的线程优先获取锁,有可能会造成优先级反转或者饥饿现象(等了好久都没获得到锁非常饥饿)。

java.util.concurrent包中的ReentrantLock类,可以在这个类中的构造方法中传入一个Boolean类型值,如果是true代表公平锁,如果false是非公平锁,什么也不传默认是非公平锁。

非公平锁的优点在于比公平锁的吞吐量要大,并发效率要高。

Synchronized就是典型的非公平锁。

可重入锁(又名递归锁)

理论:是指同一线程外层函数获得到锁之后,内层函数会自动获得到锁。
其实就是同步方法中调用另一个同步方法,线程获取到外层函数锁,内层锁自动获取到。

例如:代码如下

public class Reentrant {
    public static synchronized void method(){       System.out.println(Thread.currentThread().getName()+"mthood..");
        method01();
    }

    public static synchronized void method01(){
        System.out.println(Thread.currentThread().getName()+"method01.");
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Reentrant.method();
            }
        },"t1").start();
       
        new Thread(new Runnable() {
            @Override
            public void run() {
                Reentrant.method();
            }
        },"t2").start();
    }
}

输出结果:
t1mthood......
t1method01.....
t2mthood......
t2method01.....

最典型的可重入锁除了Synchronized外还有ReentrantLock,上边代码是Synchronized重入锁的代码。

ReentrantLock代码演示。

public class Reentrant01 {
    static Lock lock = new ReentrantLock();
    public static void set(){
       lock.lock();
       try {
           System.out.println(Thread.currentThread().getName()+"set...");
           get();
       } finally {
           lock.unlock();
       }
   }
    public static void get(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"get......");
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Reentrant01.set();
            }
        },"t3").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Reentrant01.set();
            }
        },"t4").start();
    }
}

结果:
t3set...
t3get......
t4set...
t4get......

注意:lock.lock()几次就unlock()几次,只要匹配就不会出现死锁。
可重入锁避免了死锁的发生。

自旋锁(CAS思想)

尝试获取锁的线程不会阻塞,而是采用循环的方式尝试获取锁,减少了线程上下文切换所消耗的性能,但同样如果循环次数过度会占用CPU资源。

循环+CAS(比较并交换)的思想。

AtomicReference(原子引用)类

手写自旋锁代码如下:

public class zixuan {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void Mylock(){
    do{
        Thread  thread = Thread.currentThread();  //获得当前线程
 }while(!atomicReference.compareAndSet(null,thread));//比较并交换 
    }

    public void UnMylock(){
        //看内存中是不是当前线程对象,如果是把它设置为null
      atomicReference.compareAndSet(Thread.currentThread(),null);
    }
    
}

独占锁

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

共享锁

指该锁可以被多个线程所持有。

对ReentrantReadWriteLock其实就是共享锁,其写锁是独占锁,该锁的共享锁保证并发读是非常高效的。

读读能共存(读的时候线程可以争抢)

读写互斥

写写互斥

代码如下:

/**
 * 读写锁,读写共存,读写同时进行,
 * 读和写共用一把锁,写可以保证写的原子性,中间没有加塞的现象,都是一个线程一个线程执行。
 * 读一起读,提高并发率
 */
public class ReentrantReadWLock {
    public volatile Map<String,Object> map = new HashMap<>();
    ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    /**
     * 写方法
     */
    public void put(String key,Object val){
        reentrantReadWriteLock.writeLock().lock(); //加入写锁
        try {
            System.out.println(Thread.currentThread().getName()+"正在写...");
            map.put(key,val);
            System.out.println(Thread.currentThread().getName()+"写完成...");
        }finally {
            reentrantReadWriteLock.writeLock().unlock();
        }
    }
    /**
     * 读方法
     */
    public void get(String key){
        reentrantReadWriteLock.readLock().lock(); //加入读锁
        try {
            System.out.println(Thread.currentThread().getName()+"正在读...");
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"读完成...");
        }finally {
            reentrantReadWriteLock.readLock().unlock();
        }
    }


    public static void main(String[] args) {
        ReentrantReadWLock reentrantReadWLock = new ReentrantReadWLock();
        for (int i=1;i<=5;i++){
            int temp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    reentrantReadWLock.put(temp+"",temp+"");
                }
            },String.valueOf(i)).start();
        }

        for (int i=1;i<=5;i++){
            int temp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    reentrantReadWLock.get(temp+"");
                }
            },String.valueOf(i)).start();
        }
    }
}

结果:
1正在写...
1写完成...
2正在写...
2写完成...
3正在写...
3写完成...
4正在写...
4写完成...
5正在写...
5写完成...
1正在读...
2正在读...
3正在读...
5正在读...
4正在读...
1读完成...
2读完成...
3读完成...
5读完成...
4读完成...

总结:

ReentrantReadWriteLock是共享锁,这个类里面有ReadLock()方法获得读锁,WriteLock()方法获得写锁。写的时候只能一个线程一个线程获得锁,中间不能加塞,保证写的原子性。读的时候可以多线程一起读,提高并发率。

举个例子:

写的时候为保证数据的原子性,在写的时候上锁只能由一个线程来写,写完了再由另一个线程写。不能正在写的时候被另一个线程加塞,破坏写的数据的原子性。而读就不需要,读可以多个线程一起读,就好比飞机场的公示牌如果只允许一个人一个人读岂不是太慢了,大家一起读提高效率,一个人表示一个线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值