Java锁

前言

java中的锁一般指的是互斥锁,只是一般,高阶的本文暂不讨论。

什么是互斥锁?

当多个线程对共享资源访问的时候,只能有一个线程可以获得该共享资源的锁,当线程A尝试获取线程B的锁时,线程A必须等待或者阻塞,直到线程B释放该锁为止,否则线程A将一直等待下去。

特征

1.锁只能是引用类型
2.执行线程进入synchronized块之前会自动获得锁,并且获得锁的唯一途径就是进入这个内部锁保护的同步块或方法
3.无论是通过正常语句退出还是执行过程中抛出了异常,线程都会在放弃对synchronized块的控制时自动释放锁
4.在java中实现锁机制不仅仅限于使用synchronized关键字,JDK1.5之后提供的Lock
5.一个synchronized块包含两个部分:锁对象的引用,以及这个锁保护的代码块
6.synchronized不具有继承性(在父类中加上synchronized关键字,子类重写父类方法测试一下加上synchronized关键字和不加关键字的区别即可。)

类锁/对象锁

根据使用方式的不同一般我们会将锁分为对象锁和类锁,两个锁是有很大差别的。

类锁

0.所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是[类名.class]的方式。
1.类锁是作用在静态方法或者Class对象上面的,每个类只有一个Class对象,所以类锁只有一个。
2.类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定的是实例方法还是静态方法区别的 。
3.类锁是用来控制静态方法(或静态变量互斥体)之间的同步


   // 类锁:形式1

    public class Test {
        public static synchronized void Method1() {
            System.out.println("我是类锁一号");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 类锁:形式2

    public void Method2() {
        synchronized (Test.class) {
            System.out.println("我是类锁二号");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

对象锁

1.对象锁是作用在实例方法或者一个对象实例上面的,一个类可以有多个实例对象,因此一个类的对象锁可能会有多个。
2.对象锁是用来控制实例方法之间的同步

对象锁又分为:一般对象锁和方法锁。


  // 对象锁:形式1(方法锁)

    public class Test {
        public synchronized void Method1() {
            System.out.println("我是对象锁也是方法锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 对象锁:形式2(代码块形式)

    public void Method2() {
        synchronized (this) {
            System.out.println("我是对象锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

死锁

介绍

  • 当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:
  • 线程A当前持有互斥所锁lock1
  • 线程B当前持有互斥锁lock2。
  • 接下来,当线程A仍然持有lock1时,它试图获取lock2,因为线程B正持有lock2,因此线程A会阻塞等待线程B对lock2的释放。
  • 如果此时线程B在持有lock2的时候,也在试图获取lock1,因为线程A正持有lock1,因此线程B会阻塞等待A对lock1的释放。
  • 二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。

代码


package com.zj.多线程.sychronized.死锁;

public class DeadLock {

    //1:2个不同的锁
    //2:任何一方都没法释放对方所需要的锁,因为双方要释放对方所需要的锁的条件就是对方的锁。
    /*
     * A和B
     * A说,我想把我的锁给你,但是没有你的锁,我没法给你
     * 再比如,2扇门,A的门把B关外面,B的门把A关外面
     * */

    public static void main(String[] args) throws InterruptedException {

        final Lock lock1 = new Lock();
        final Lock lock2 = new Lock();


        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (lock1) {
                        System.out.println("thread 111 out");
                        synchronized (lock2) {
                            System.out.println("thread 111 in");
                        }
                    }
//                    System.out.println("thread 111 in");
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (lock2) {
                        System.out.println("thread 222 out");
                        synchronized (lock1) {
                            System.out.println("thread 222 in");
                        }
                    }
//                    System.out.println("thread 222 in");
                }

            }
        }).start();

    }


    //锁对象
    static class Lock {

        Lock() {
        }

    }

}

规避

  • 只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;
  • 尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;
  • 创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;

Thanks

参考
进阶1
进阶2
本文Demo

猜你喜欢

转载自blog.csdn.net/user11223344abc/article/details/80409272
今日推荐