26、多线程锁

6.1 锁的八种情况

1、 两个同步方法,一个对象,两个线程,分别调用getOne()和getTwo(),打印?//12;

2、 在getOne()方法中添加睡眠3秒,打印?//12;

3、 添加非同步方法,启动三个线程,打印?//312;

4、 创建两个对象,一个调用getOne()一个调用getTwo(),打印?//21;

5、 将getOne()修改为静态同步方法,一个对象,打印?//21;

6、 将getTwo()修改为静态同步方法,一个对象,打印?//12;

7、 将getTwo()修改为非静态同步方法,两个对象,打印?//21;

8、 两个静态同步方法,两个对象,打印?//12;

总结:

  • 对于普通同步方法:锁的是当前对象
  • 对于静态同步方法:锁的是类
  • 对象锁和类锁不是同一把锁
  • 对于同步方法块:锁的是synchronized括号里配置的对象

6.2 公平锁和非公平锁

非公平锁:

是指多个线程获取锁的顺序,并不是按照申请锁的顺序,有可能申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转,或者饥饿的线程(也就是某个线程一直得不到锁)

效率高,但是会造成线程饿死

公平锁:

是指多个线程按照申请锁的顺序来获取锁,类似于排队买饭,先来后到,先来先服务,就是公平的,也就是队列

private final ReentrantLock lock = new ReentrantLock(true);

6.3 可重入锁(递归锁)

什么是 “可重入”,可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。

synchronizedReentrantLock都是可重入锁。

synchronized是隐式的可重入锁,ReentrantLock是显式的可重入锁。

synchronized代码示例:

public static void main(String[] args) {
   
     
    //synchronized
    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() + " 内层");
                }
            }
        }
    }, "t1").start();
}

ReentrantLock代码示例:

public static void main(String[] args) {
   
     
	//lock
	Lock lock = new ReentrantLock();
	
	new Thread(()->{
   
     
	    try{
   
     
	      lock.lock();
	      System.out.println(Thread.currentThread().getName() + "外层");
	
	      try {
   
     
	          lock.lock();
	          System.out.println(Thread.currentThread().getName() + "中层");
	
	          try {
   
     
	              lock.lock();
	              System.out.println(Thread.currentThread().getName() + "内层");
	          } finally {
   
     
	              lock.unlock();
	          }
	
	      } finally {
   
     
	          lock.unlock();
	      }
	
	    } finally {
   
     
	        lock.unlock();
	    }
	}, "t1").start();
 }

注意:有几个lock()就需要有几个unlock(),否则其他线程会拿不到锁。

6.4 死锁

1、 什么是死锁?;

两个或两个以上的进程,因为资源争夺的问题,而造成互相等待的现象。如果没有外力干涉,这些线程都不会继续执行下去。

2、 产生死锁的原因:

  • 系统资源不足
  • 资源分配不当
  • 进程推进顺序不合理

3、 死锁实例:

package lock;

public class DeadLock {
   
     

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

        new Thread(()->{
   
     
            synchronized (a) {
   
     
                System.out.println(Thread.currentThread().getName() + "获取了锁a,想要获取锁b");
                try {
   
     
                    Thread.sleep(1000);
                } 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");

                synchronized (a) {
   
     
                    System.out.println(Thread.currentThread().getName() + "获取了锁a");
                }
            }
        }, "B").start();
    }
}

如何查看死锁:
1、 terminal中输入jsp-l(这个命令在jdk的bin目录中),查看java的进程号;
2、 jstack[进程号],往下翻,可以找到以下信息: