JAVA中锁是
在并发编程中,经常会遇到多个线程访问同一个共享变量,当同时对共享变量进行读写操作时,就会产生数据不一致的情况。
为了解决这个问题
- JDK 1.5 之前,使用 synchronized 关键字,拿到 Java 对象的锁,保护锁定的代码块。JVM 保证同一时刻只有一个线程可以拿到这个 Java 对象的锁,执行对应的代码块。
- JDK 1.5 开始,引入了并发工具包 java.util.concurrent.locks.Lock,让锁的功能更加丰富。
常见的锁
- synchronized 关键字锁定代码库
- 可重入锁 java.util.concurrent.lock.ReentrantLock
- 可重复读写锁 java.util.concurrent.lock.ReentrantReadWriteLock
Java 中不同维度的锁分类
可重入锁
- 指在同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁。JDK 中基本都是可重入锁,避免死锁的发生。上面提到的常见的锁都是可重入锁。
公平锁 / 非公平锁
- 公平锁,指多个线程按照申请锁的顺序来获取锁。如 java.util.concurrent.lock.ReentrantLock.FairSync
- 非公平锁,指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程先获得锁。如 synchronized、java.util.concurrent.lock.ReentrantLock.NonfairSync
独享锁 / 共享锁
- 独享锁,指锁一次只能被一个线程所持有。synchronized、java.util.concurrent.locks.ReentrantLock 都是独享锁
- 共享锁,指锁可被多个线程所持有。ReadWriteLock 返回的 ReadLock 就是共享锁
悲观锁 / 乐观锁
- 悲观锁,一律会对代码块进行加锁,如 synchronized、java.util.concurrent.locks.ReentrantLock
- 乐观锁,默认不会进行并发修改,通常采用 CAS 算法不断尝试更新
- 悲观锁适合写操作较多的场景,乐观锁适合读操作较多的场景
粗粒度锁 / 细粒度锁
- 粗粒度锁,就是把执行的代码块都锁定
- 细粒度锁,就是锁住尽可能小的代码块,java.util.concurrent.ConcurrentHashMap 中的分段锁就是一种细粒度锁
- 粗粒度锁和细粒度锁是相对的,没有什么标准
偏向锁 / 轻量级锁 / 重量级锁
- JDK 1.5 之后新增锁的升级机制,提升性能。
- 通过 synchronized 加锁后,一段同步代码一直被同一个线程所访问,那么该线程获取的就是偏向锁
- 偏向锁被一个其他线程访问时,Java 对象的偏向锁就会升级为轻量级锁
- 再有其他线程会以自旋的形式尝试获取锁,不会阻塞,自旋一定次数仍然未获取到锁,就会膨胀为重量级锁
自旋锁
- 自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环占有、浪费 CPU 资源
synchronized与Lock的区别与使用?
技术点:
1、线程与进程:线程是程序执行流的最小单位,而进程是系统进行资源分配和调度的一个独立单位。
2、Thread的几个重要方法:
- start()方法,调用该方法开始执行该线程;
- stop()方法,调用该方法强制结束该线程执行;
- join方法,调用该方法等待该线程结束。
- sleep()方法,调用该方法该线程进入等待。
- run()方法,调用该方法直接执行线程的run()方法,但是线程调用start()方法时也会运行run()方法,区别就是一个是由线程调度运行run()方法,一个是直接调用了线程中的run()方法!!
那wait()和notify()呢?
要注意,其实wait()与notify()方法是Object的方法,不是Thread的方法!!同时,wait()与notify()会配合使用,分别表示线程挂起和线程恢复。
wait()与sleep()的区别,简单来说wait()会释放对象锁而sleep()不会释放对象锁。
线程总共有5大状态:
-
新建状态:新建线程对象,并没有调用start()方法之前
-
就绪状态:调用start()方法之后线程就进入就绪状态,但是并不是说只要调用start()方法线程就马上变为当前线程,在变为当前线程之前都是为就绪状态。值得一提的是,线程在睡眠和挂起中恢复的时候也会进入就绪状态哦。
-
运行状态:线程被设置为当前线程,开始执行run()方法。就是线程进入运行状态
-
阻塞状态:线程被暂停,比如说调用sleep()方法后线程就进入阻塞状态
-
死亡状态:线程执行结束
1.为什么要使用synchronized
在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
2.实现原理
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
3.synchronized的三种应用方式
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
- 普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
- 静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
- 同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
4.synchronized的作用
Synchronized是Java中解决并发问题的一种最常用最简单的方法 ,他可以确保线程互斥的访问同步代码