先看一段程序:
public class DemoT {
// 定义一个共享变量
private static int count = 0;
// 用来操作共享变量的方法
public static void inc() {
try {
// 为了展示出多线程同时操作同一数据会出现问题。如果去掉之后可能会正常计算出数据
Thread.sleep(1);
count++;
// decr();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) throws InterruptedException {
// 新建三个线程 每个循环100遍,去调用inc方法操作共享变量
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread.start();
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread3.start();
// 保证三个子线程已经运行结束
Thread.sleep(2000);
// 打印共享变量
System.out.println("count:" + count);
}
}
想要的结果:(打印:count:300)
实际打印结果:count:285(可能不是285,但是基本上都是小于300)
添加重入锁(ReentrantLock)(还是之前的代码只不过加了一把锁)
public class DemoT {
// 定义一个共享变量
private static int count = 0;
//添加重入锁 @param fair {@code true} if this lock should use a fair ordering policy(译:@param fair{@code true}如果此锁应使用公平排序策略)
// 如果添加true 则是使用公平锁,否则为非公平锁
static Lock lock = new ReentrantLock(true);
// 用来操作共享变量的方法
public static void inc() {
lock.lock(); //获得锁(互斥锁)
try {
// 为了展示出多线程同时操作同一数据会出现问题。如果去掉之后可能会正常计算出数据
Thread.sleep(1);
count++;
// decr();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁
}
}
public static void main(String[] args) throws InterruptedException {
// 新建三个线程 每个循环100遍,去调用inc方法操作共享变量
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread.start();
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread3.start();
// 保证三个子线程已经运行结束
Thread.sleep(2000);
// 打印共享变量
System.out.println("count:" + count);
}
}
运行得到:count:300
实现想要的结果,所以这是一个互斥锁,只允许一个线程进入当前范围
那为什么他叫“重入”呢?是因为他可以获得当前锁的情况下,还可以进入另外一个以当前锁为锁头的代码块。例如下面代码:(还是之前代码,只不过添加了一个方法体用来对共享变量递减)
public class DemoT {
// 定义一个共享变量
private static int count = 0;
//添加重入锁 @param fair {@code true} if this lock should use a fair ordering policy(译:@param fair{@code true}如果此锁应使用公平排序策略)
// 如果添加true 则是使用公平锁,否则为非公平锁
static Lock lock = new ReentrantLock(true);
// 用来操作共享变量的方法
public static void inc() {
lock.lock(); //获得锁(互斥锁)
try {
// 为了展示出多线程同时操作同一数据会出现问题。如果去掉之后可能会正常计算出数据
Thread.sleep(1);
count++;
decr();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁
}
}
public static void decr() {
lock.lock(); //state=2 //ThreadA再次来抢占锁 : 不需要再次抢占锁,而是只增加重入的次数
try {
count--;
} finally {
lock.unlock(); //state=1
}
}
public static void main(String[] args) throws InterruptedException {
// 新建三个线程 每个循环100遍,去调用inc方法操作共享变量
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread.start();
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread3.start();
// 保证三个子线程已经运行结束
Thread.sleep(2000);
// 打印共享变量
System.out.println("count:" + count);
}
}
预计结果:count:0
实际运行结果:count:0
重入是如何实现的?
内部有一个 volatile 修饰的变量,当当前线程添加一把锁变量+1 ,当变量结束一个锁,则-1;当为0 说明当前线程无当前锁 volatile int waitStatus;
没有获取到当前锁的线程怎么操作?以及是怎么保存的?
会放在一个双向链表 使用 addWaiter 将未获得锁的线程加入到队列
并且区分公平锁和非公平锁
非公平锁:如果当前线程执行到锁的位置,直接去抢占锁,如果抢到,直接拿到锁,运行。链表队列中继续等待。
公平锁:如果当前线程执行到锁的位置,先去链表中判断是否存在其他线程,如果存在,加入队列末尾。如果不存在,去判断锁是否被其他线程拿到,如果有,添加到链表队列,没有执行代码块。