常见锁介绍

synchronized锁的八中情况

package com.shaonian.juc.more_thread_lock;

import java.util.concurrent.TimeUnit;

class Phone {

    public static synchronized void sendSMS() throws Exception {
        //停留4秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}

/**
 * @Description: 8锁
 *
1 标准访问,先打印短信还是邮件
------sendSMS
------sendEmail

2 停4秒在短信方法内,先打印短信还是邮件
------sendSMS
------sendEmail

3 新增普通的hello方法,是先打短信还是hello
------getHello
------sendSMS

4 现在有两部手机,先打印短信还是邮件
------sendEmail
------sendSMS

5 两个静态同步方法,1部手机,先打印短信还是邮件
------sendSMS
------sendEmail

6 两个静态同步方法,2部手机,先打印短信还是邮件
------sendSMS
------sendEmail

7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
------sendEmail
------sendSMS

8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
------sendEmail
------sendSMS

 */

public class Lock8QuestionBySynchronized {
    public static void main(String[] args) throws Exception {

        Phone phone = new Phone();
        Phone phone2 = new Phone();

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

        Thread.sleep(100);

        new Thread(() -> {
            try {
                // phone.sendEmail();
                // phone.getHello();
                phone2.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "BB").start();
    }
}

代码结果原因分析,见博客中synchronized关键字回顾。

ReentrantLock读写锁分析

/**
     * 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) {//传入false,定义的就是非公平的读写锁
        sync = fair ? new FairSync() : new NonfairSync();
    }
非公平锁(ReentrantLock实现)

非公平锁是多个线程加锁时直接尝试获取锁,能抢到锁到直接占有锁,抢不到才会到等待队列的队尾等待。确定可能会出现线程饿死现象(锁一直被一个线程持有,某些线程一直拿不到锁),优点是执行效率高,可以直接获取锁。

static final class NonfairSync extends Sync {//ReentrantLock中的静态内部类
	private static final long serialVersionUID = 7316153563782823691L;

/**
 * Performs lock.  Try immediate barge, backing up to normal
 * acquire on failure.
 */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
公平锁(ReentrantLock实现)

公平锁是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。保证每个线程都能拿到锁,优点是执行效率比非公平锁低,优点是不会出现线程饿死现象。

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

可重入锁(ReentrantLock实现)

synchronized(隐式)和Lock(显式)都是可重入锁,也叫递归锁。特点,类似于,有了第一把锁,此锁内部锁的代码都可以访问。像开了大门,开剩下的门就不用再用钥匙开门了。

/**
 * @author 长名06
 * @version 1.0
 * 可重入锁案例
 */
public class ReentrantLockDemo {

    private synchronized void add(){
        add();
    }


    public static void main(String[] args) {

        //new ReentrantLockDemo().add();//会出现StackOverflowError

        Object o = new Object();

        new Thread(() -> {
            synchronized (o){
                System.out.println(Thread.currentThread().getName() + "外部");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + "中部");
                    synchronized (o){
                        System.out.println(Thread.currentThread().getName() + "内部");
                    }
                }
            }
        },"aa").start();
        
        ReentrantLock lock = new ReentrantLock();
        new Thread(() -> {
            try{
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "外层操作");
                try{
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + "内层操作");

                }finally {
                    lock.unlock();
                }
            }finally {
                lock.unlock();
            }
        }).start();
     //结果
     //Thread-0外层操作
     //Thread-0内层操作
    }
}

死锁

基本介绍

死锁,是一种多个线程在执行过程中,因竞争资源时,导致线程互相持有其他线程执行需要的锁,从而引起线程等待的现象。在没有外力的作用下,程序会崩溃。

引起死锁的原因
  • 1.系统资源不足。
  • 2.进程运行顺序不合适。
  • 3.资源分配不当。
/**
 * @author 长名06
 * @version 1.0
 * 死锁代码
 */
public class DeadLock {

    static Object a = new Object();
    static Object b = new Object();
    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (a){
                System.out.println(Thread.currentThread().getName() + "持有锁a,试图后去锁b");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b){
                    System.out.println(Thread.currentThread().getName() + "获取锁b");
                }
            }
        },"a").start();

        new Thread(() -> {
            synchronized (b){
                System.out.println(Thread.currentThread().getName() + "持有锁b,试图后去锁a");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println(Thread.currentThread().getName() + "获取锁a");
                }
            }
        },"b").start();
    }
}
验证线程一直等待是否是死锁
  • 1.jps -l 命令类似linux下的ps - ef 可以查询java的线程
    多线程锁-小白菜博客
    正在执行的死锁代码的进程id
  • 2.jstack工具(JVM 自带的堆栈工具) jstack 线程id 可以查询对应线程的信息
    执行jstack pid指令
    提示语句中会有 Found 1 deadlock

只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。