java并发锁

乐观锁与悲观锁

乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。

乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。

乐观锁的特点:

ABA 问题:如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?

很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 “ABA” 问题。

循环时间长开销大:在特定场景下会有效率问题。

自旋 CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给 CPU 带来非常大的执行开销。

atomic实现乐观锁

public class DemoTest extends Thread{
    //Atomic 操作,引入AtomicInteger。这是实现乐观锁的关键所在。
    private static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    //每个线程让count自增100次
                    for (int i = 0; i < 100; i++) {
                        count.incrementAndGet();

                    }
                }
            }). start();
        }
        try{
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(count);
    }
}
synchronized实现悲观锁
public class DemoTest01 extends Thread{
    private static int count = 0; //定义count = 0
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) { //通过for循环创建两个线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    //每个线程让count自增100次
                    for (int i = 0; i < 100; i++) {
                        synchronized (DemoTest.class){
                            count++;
                        }
                    }
                }
            }). start();
        }
        try{
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(count);
    }
}

 

独占锁与共享锁

独占锁:每次只能有一个线程持有锁,比如 ReentrantLock 就是以独占方式实现的互斥锁;
共享锁:允许多个线程同时获取锁,并发访问共享资源,比如 ReentrantReadWriteLock。

 

公平锁与非公平锁

公平锁:表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。

非公平锁:非公平锁则在运行时闯入,不遵循先到先执行的规则。

//公平锁
ReentrantLock pairLock = new ReentrantLock(true);
//非公平锁
ReentrantLock pairLock1 = new ReentrantLock(false);
//如果构造函数不传递参数,则默认是非公平锁。
ReentrantLock pairLock2 = new ReentrantLock();

 

自旋锁

自旋锁:自旋锁则是当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃 CPU 使用权的情况下,多次尝试获取(默认次数是 10,可以使用-XX:PreBlockSpinsh 参数设置该值)。

可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者 class),不会因为之前已经获取过还没释放而阻塞。

Java 中 ReentrantLock 和 synchronized 都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

ReentrantLock 重入验证

/**
 * @author wsy 
 */
public class DemoTest03 {
    //ReentrantLock可重入验证

    public static void main(String[] args) {

        new Thread(new ReentrantLockTest()).start();
    }
}

class ReentrantLockTest implements Runnable {

    Lock lock = new ReentrantLock();

    public void helloA() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " helloA()");
            helloB();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }

    public void helloB() {
        lock.lock();
        try {

            System.out.println(Thread.currentThread().getName() + " helloB()");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }


    @Override
    public void run() {
        helloA();

    }
}

synchronized 可重入锁验证

public class DemoTest04 {
    //synchronized可重入锁验证
    public static void main(String[] args) {
        new Thread(new SycDemo()).start();
    }

}

class SycDemo implements Runnable{
    public synchronized void helloA(){

        System.out.println(Thread.currentThread().getName() + " helloA()");
        helloB();

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


    }


    @Override
    public void run() {
        helloA();
    }
}

 

posted @ 2023-03-31 15:08  永哥伟大  阅读(46)  评论(0)    收藏  举报