首先我们可以先写一个锁的测试类,来让自己的锁实现好后。可以确保有效。
public class Sequence {
private Lock lock = new MLock();
private int value;
public int getNext(){
lock.lock();
value++;
lock.unlock();
return value;
}
}
接下来我们思考,加锁是一个什么效果。第一个线程进来可以获得锁,之后进来的线程应该等待,直到第一个线程把锁释放。
那么我们首先还是需要对LOCK这个方法做SYNCHRONIZED保护。随后可以维护一个BOOLEAN来表示是否是第一个线程。其余的就调用WAIT就可以实现阻塞的效果。
private boolean isLocked = false;
@Override
public synchronized void lock() {
while(isLocked){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLocked = true;
}
那么LOCK对应的UNLOCK就应该用NOTIFY就好了。
@Override
public synchronized void unlock() {
isLocked = false;
notify();
}
在sequence里写下MAIN方法
public static void main(String[] args) {
Sequence s = new Sequence();
for(int i = 0; i < 5; i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println(s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
发现没啥问题,
我们可以把锁去掉,再看看。答案只有48了,少了2.
接下来我们来实现可以重入的锁。
先写个测试类
Lock lock = new MLock();
public void a(){
lock.lock();
System.out.println("a");
b();
lock.unlock();
}
public void b(){
lock.lock();
System.out.println("b");
lock.unlock();
}
public static void main(String[] args) {
Demo d = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
d.a();
}
}).start();
}
如果我们直接运行发现输出A之后就会无限期等待了。
之所以会这样,是因为同一个进程先调了2次LOCK,再第2次就卡主了。所以我们只要记住持有锁的当前线程。
lock改造如下
Thread lockBy = null;
int lockCount = 0;
@Override
public synchronized void lock() {
Thread current = Thread.currentThread();
if(isLocked && current != lockBy){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLocked = true;
lockBy = current;
lockCount++;
}
unlock 改造如下
@Override
public synchronized void unlock() {
if(lockBy != Thread.currentThread()){
return;
}
lockCount--;
if(lockCount == 0){
isLocked = false;
notify();
lockBy = null;
}
}
再跑一遍原来的测试代码就可以过了。