java中的锁

公平锁与非公平锁

公平锁:每个线程获取锁的机会是公平的,按照申请锁的顺序将线程放进一个FIFO队列中,谁先申请锁谁就先获取锁,每次有线程来抢占锁的时候,都会检查有没有等待队列,如果有,则将该线程加入等待队列

非公平锁:线程获取锁的机会是不公平的,获取锁的线程会随机抢占

synchronized实现的锁是一种非公平锁,公平锁可以通过ReentrantLock来实现,同时ReentrantLock也可以实现非公平锁

1
2
3
4
5
6
7
8
// 默认是非公平锁
Lock lock = new ReentrantLock();
//public ReentrantLock() {
// sync = new NonfairSync();
//}

// 公平锁
Lock lock = new ReentrantLock(true);

可重入锁

可重入锁指在同一个线程中外部方法持有的锁可以被内部方法获取

synchronized和ReentrantLock都是可以实现可重入锁

  • synchronized实现的可重入锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestReEntryLock {
public synchronized void set() {
System.out.println(Thread.currentThread().getName()+"set");
get();
}

public synchronized void get() {
System.out.println(Thread.currentThread().getName()+"get");
set();
}

public static void main(String[] args) {
TestReEntryLock lock = new TestReEntryLock();
new Thread(()->{
lock.set();
},"t1").start();
}
}
  • ReentrantLock
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
public class TestReEntryLock {

Lock lock = new ReentrantLock();
public void set() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"\t set");
get();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"\t get");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TestReEntryLock lock = new TestReEntryLock();
new Thread(()->{
lock.set();
},"t1").start();
}
}

读写锁

支持共享读,不支持共享读写,和写。也就是说支持同时读,但是不支持同时写。这种锁可以提高读的并发性

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
public class WriteAndReadLock {

ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

// 写操作
public void write() {
rwl.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"正在写");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
rwl.writeLock().unlock();
System.out.println(Thread.currentThread().getName()+"写已经完成");
}
// 读操作
public void read() {
rwl.readLock().lock();
System.out.println(Thread.currentThread().getName()+"正在读");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"读已经完成");
rwl.readLock().unlock();
}
public static void main(String[] args) {
WriteAndReadLock wad = new WriteAndReadLock();
for (int i = 0; i < 3; i++) {
new Thread(()->{
wad.write();
}).start();
}
for (int i = 0; i < 3; i++) {
new Thread(()->{
wad.read();
}).start();
}
}
}

结果:

​ Thread-0正在写
Thread-0写已经完成
Thread-1正在写
Thread-1写已经完成
Thread-2正在写
Thread-2写已经完成
Thread-3正在读
Thread-4正在读
Thread-5正在读
Thread-3读已经完成
Thread-4读已经完成
Thread-5读已经完成

结论: 每个线程写操作不能被其它操作所打断,不支持共享写。读操作支持共享读

自旋锁

自旋锁在没有获取到锁的情况下不会马上将线程阻塞,而是不断尝试获取锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RoteBySelf {

private AtomicReference<Thread> ar = new AtomicReference<Thread>();

// 手写一个自旋锁
public void lock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"\t 尝试获取锁");
// 如果是第一次则获取锁成功,跳出while循环
while(!ar.compareAndSet(null,thread)) {
}
System.out.println(thread.getName()+"\t 获取锁成功");
}

public void unlock() {
Thread thread = Thread.currentThread();
ar.compareAndSet(thread, null);
System.out.println(thread.getName()+"\t 释放锁成功");
}
}

死锁

死锁是多个线程争抢共享资源导致互相等待的一种状态,如果没有外力驱使,那么该状态会一直存在。

产生死锁的条件:

  • 互斥:一个资源要么被抢占,要么可用,必须是临界资源
  • 请求和保持:线程请求到资源后,继续申请资源,并且保持原来所持有的资源
  • 不可抢占:已经被分配的资源不可被抢占
  • 环路等待:线程之间互相等待对方释放所持有的资源
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
public class DeadDemo implements Runnable{

private Account from;
private Account to;
private int amount;
public DeadDemo(Account from, Account to, int amount) {
this.from = from;
this.to = to;
this.amount = amount;
}
public void run() {
synchronized(from) {
synchronized (to) {
from.amount = from.amount-amount;
to.amount = to.amount+10;
System.out.println("success");
}
}
}
public static void main(String[] args) {
Account a1 = new Account();
Account a2 = new Account();
new Thread(new DeadDemo(a1,a2,10)).start();
new Thread(new DeadDemo(a2,a1,10)).start();
}
}
class Account{
int amount;
}
//死锁排查
jps -l
jstack 进程号
文章目录
  1. 1. 公平锁与非公平锁
  2. 2. 可重入锁
  3. 3. 读写锁
  4. 4. 自旋锁
  5. 5. 死锁
|
载入天数...载入时分秒...