Java多线程与并发_Java锁

Java多线程与并发_Java锁

累吗?累就对了,说明你还活着

一、公平锁与非公平锁

公平锁:是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。在高并发的情况下,有可能会造成优先级反转或者饥饿现象

并发包中ReentrantLock的创建可以指定构造函数boolean类型来得到公平锁或非公平锁,默认是非公平锁

    /**
     * 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();
    }

两者区别

  • 公平锁:就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占优锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己

  • 非公平锁:非公平锁不比较粗鲁,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式

二、可重入锁(递归锁)

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁

也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块

class Phone implements Runnable{
    public synchronized void sendSMS() {
        System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
        sendEmail();
    }

    public synchronized void sendEmail() {
        System.out.println(Thread.currentThread().getName() + "\t ########invoked sendEmail()");
    }

    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        get();
    }

    public void get() {
        lock.lock();
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t invoked get()");
            set();

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void set() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t invoked set()");

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
    Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.sendSMS();
            }catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();

        new Thread(() -> {
            try {
                phone.sendSMS();
            }catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();

sychoronized可重入锁结果:

t1	 invoked sendSMS()             t1线程在外层方法获取锁的时候
t1	 ########invoked sendEmail()   t1在进入内层方法会自动获取锁
t2	 invoked sendSMS()
t2	 ########invoked sendEmail()

Process finished with exit code 0
    Thread t3 = new Thread(phone,"t3");
        Thread t4 = new Thread(phone,"t4");
        t3.start();
        t4.start();

ReentrantLock可重入锁结果:

t3	 invoked get()
t3	 invoked set()
t4	 invoked get()
t4	 invoked set()

Process finished with exit code 0

三、自旋锁

指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文的消耗,缺点是循环会消耗CPU

实现一个自旋锁

自旋锁好处:循环比较获取直到成功为止,没有类似wait的阻塞
通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒,B随后进来后发现
当前有线程持有锁,不是null,所以只能通过自旋等待,直到A释放锁后B随后抢到
public class SpinLockDemo {

    //原子引用线程
    AtomicReference atomicReference = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t come in");
        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) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();
        },"AA").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        },"BB").start();

    }
}

结果

AA	 come in
BB	 come in
AA	 invoked myUnLock()
BB	 invoked myUnLock()

Process finished with exit code 0

你可能感兴趣的:(Java多线程与并发)