线程冲突: 多个线程并发并行的对同一个共享变量进行操作(存在线程安全问题)
乐观锁与悲观锁
乐观锁: 以乐观的心态来看待线程冲突,总是觉得不会有多个线程同时操作同一个共享变量,所以每次都不加锁,直接操作共享变量.
悲观锁:以悲观的心态来看待线程冲突,总是觉得会有其他线程同时操作共享变量,所以每次都加锁操作共享变量.
乐观锁适用于大部分情况下不存在线程冲突的场景,而悲观锁适用于大部分情况下存在线程冲突的场景.
读写锁
多个线程之间,读读操作不会产生线程安全问题,但读操作和写操作之间需要进行互斥,否则会产生线程安全问题,如果两种情况下都使用同一种锁,就会产生极大的性能消耗,因此出现了读写锁.
读写锁就是把读操作和写操作区分对待,java标准库中提供了ReentrantReadWriteLock类来实现了读写锁
在读写锁的使用中:
· 读加锁和读加锁之间不互斥
· 读加锁和写加锁之间互斥
· 写加锁和写加锁之间互斥
使用示例
public class LockDemo {
public static void main(String[] args) {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();//读锁
ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();//写锁
//写操作
writeLock.lock();//写锁加锁
//写
writeLock.unlock();//写锁释放锁
//读操作
readLock.lock();//读锁加锁
//读
readLock.unlock();//读锁释放锁
}
}
读写锁特别适合与“频繁读,不频繁写”的场景
自旋锁
自旋锁通常是结合CAS来实现的,以此来保证线程安全的修改变量操作.
通常情况下,线程在抢占锁失败后就进入了BLOCKED状态,放弃CPU,需要再过很久才可能被调度的.但是大部分情况下,虽然抢占锁失败,但过不了多久锁就会被释放,因此没有必要放弃CPU,这时就可以通过自旋锁来处理这样的问题
自旋锁的原理:如果获取锁失败,立即再次尝试获取,无限循环,直到获取到锁为止,一旦锁被其他线程释放,就能立刻获取到锁.
自旋锁的优缺点:
优点:没有放弃CPU,不涉及线程的阻塞和调度,一旦锁被释放,就能第一时间获取到锁
缺点:如果锁被其他线程长时间占有,就会持续的消耗CPU资源
公平锁与非公平锁
公平锁:以申请锁时间的先后顺序来获取锁,类似于排队买票
优点:线程以申请锁的先后顺序来获取锁,不会出现争抢的情况,也不会出现线程饥饿.
缺点:效率较低,等待队列中除一个线程外,其他线程都会阻塞,CPU唤醒线程的开销比非公平锁大
非公平锁:以竞争的方式来获取锁
优点:不考虑执行顺序,效率高
缺点: 可能出现线程饥饿的现象
例如synchronized申请锁对象时,就是非公平锁.
可重入锁与不可重入锁
一个线程可以多次获取同一个锁就是可重入锁,反之就是不可重入锁
ReentrantLock和synchronized都是可重入锁
例如下列代码,就是可重入锁synchronized的示例
package threadhomework;
/**
* Created with IntelliJ IDEA.
* Description: 可重入锁示例
* User: Li_yizYa
* Date: 2022—09—23
* Time: 22:51
*/
public class MyRunnable implements Runnable {
public synchronized void set() {
System.out.println(Thread.currentThread().getName());
get();
}
public synchronized void get() {
System.out.println(Thread.currentThread().getName());
}
@Override
public void run() {
set();
}
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
while(true) {
new Thread(r).start();
}
}
}