第五章-谈谈对锁的理解

公平锁和非公平锁

是什么

公平锁:是指多线程按照申请锁的顺序来获取锁,先来后台

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后来先到,在高并发情况下,有可能会造成优先级反转或者饥饿现象

两者区别

公平锁/非公平锁:并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁

两者区别:
公平锁:锁被占用,加入等待队列,按照FIFO的规则从队列中取到自己
非公平锁:锁被占用,直接无视,尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式。

其它

Java ReentrantLock
通过构造函数指定该锁是否是公平锁,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大。对于Synchronized而言,也是一种非公平锁。

可重入锁(递归锁)

可重入锁(也叫递归锁)

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,
当同一个线程在外层方法获取锁时,在进入内层方法会自动获取锁。

也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。

特点:

  1. ReentrantLock和Synchronized就是典型的可重入锁

  2. 可重入锁最大的作用是避免死锁

  3. 案例

    image-20191107104613872

image-20191107104623527

自旋锁

image-20191107104755824

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @desc SpinLockDemo
* @Author xw
* @Date 2019/8/29
*/
public class SpinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock() {
Thread thread = Thread.currentThread();
// System.out.println(thread.getName() + "\t come in (...)");
while (!atomicReference.compareAndSet(null, thread)) {

}
}

public void myUnlock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
// System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");
}

public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
MyData myData = new MyData();
for (int i = 1; i <= 10000; i++) {
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
spinLockDemo.myLock();
for (int j = 1; j <= 100 ; j++) {
myData.addPlusPlus();
}
spinLockDemo.myUnlock();
}, String.valueOf(i)).start();
}
while (Thread.activeCount() > 2) {

}
System.out.println(Thread.currentThread().getName() + "\t mission is over value:" + myData.number);
}
}
class MyData {
int number;
public void addPlusPlus() {
this.number ++;
}
}

独占锁(写)/共享锁(读)

读写锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* @desc 读写锁
* 多个线程操作一个资源类,所以为了满足并发量,读取共享资源应该可以同时进行。
* 写操作:原子 + 独占,整个过程必须是一个整体,中间不能被分解、打断
* 小总结:
* 读读共存
* 读写互斥
* 写写互斥
* @Author xw
* @Date 2019/8/20
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() -> {
myCache.put(tempInt + "", tempInt + "");
}, String.valueOf(i)).start();
}

for (int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() -> {
myCache.get(tempInt + "");
}, String.valueOf(i)).start();
}

}
}

class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 写
public void put(String key, Object value) {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写入完成:" + key);
lock.writeLock().unlock();
}
// 读
public void get(String key) {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "\t 正在读取:" + key);
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
Object value = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + value);
lock.readLock().unlock();
}
}

sychronized与ReentrantLock区别

image-20191107105047912