最近看了AQS底层的实现,模仿着写了一个简单的AQS锁,本来想怒装一B,结果中间出现各种坑,把经历贴出来。
记录一次踩坑过程
首先,贴出代码如下:
锁代码如下:
public class TestMyAQSLock{
private SimpleSync simpleSync = new SimpleSync ();
public void lock(){
simpleSync.lock(1);
}
public void unLock(){
simpleSync.tryRelease(0);
}
private static final class SimpleSync extends AbstractQueuedSynchronized{
@Override
protected boolean tryAcqurie(int arg){
return compareAndSetState(0,arg);
}
@Override
protected boolean tryRelease(int arg){
setState(arg);
return true;
}
@Override
protected boolean isHeldExclusively(){
return getState() == 1;
}
}
}
测试代码如下:
public class TestForMyAQSLock{
public static void main(String[] args){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor (
5,10,1L,TimeUnit.SECONDS, new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunPolicy() );
for(int i=0; i<100; i++){
Singer singer = new Singer();
threadPoolExecutor.execute(singer);
}
threadPoolExecutor.shutdown();
}
static class Singer extends Thread{
TestMyAQSLock testMyAQSLock = new TestMyAQSLock();
private static volatile int num = 1;
public Singer(){}
@Override
public void run(){
testMyAQSLock.lock();
System.out.println("编号"+num+"歌手在唱歌")
num++;
testMyAQSLock.unLock();
}
}
}
打印结果如下:
编号3歌手在唱歌.
编号3歌手在唱歌
编号10歌手在唱歌
编号27歌手在唱歌
编号8歌手在唱歌
…
编号100歌手在唱歌
编号100歌手在唱歌
第一次写多线程的我满脸问号难道是写的锁有问题,决定换成synchronized锁排查一下
修改代码如下:
static class Singer extends Thread{
private static volatile int num = 1;
public Singer(){}
@Override
public void run(){
synchronized(Thread.currentThread()){
System.out.println("编号"+num+"歌手在唱歌")
num++;
}
}
}
并没有任何效果,心态崩了。没办法,第一次写线程代码还是要排查下去
仔细分析代码,先分析为什么加的synchronize好像没有起作用。经过仔细分析
原来问题出现在这里
for(int i=0; i<100; i++){
Singer singer = new Singer();
threadPoolExecutor.execute(singer);
}
这部分代码线程不安全,每一个线程singer执行start的时候,循环并不终止,所以会导致多个线程去执行num++
虽然我对每一个线程内部执行了同步锁,但是并不能保证多线程情况下的安全。
根本原因在于锁的对象加错了。
num为静态成员变量,属于全体对象所有,若想多线程情况下保证操作安全,需要将该类加锁,而不是类对象加锁。
修改后代码如下:
static class Singer extends Thread{
private static volatile int num = 1;
public Singer(){}
@Override
public void run(){
synchronized((Singer.class)){
System.out.println("编号"+num+"歌手在唱歌")
num++;
}
}
}
打印结果如下:
编号3歌手在唱歌.
编号2歌手在唱歌
编号4歌手在唱歌
编号1歌手在唱歌
编号5歌手在唱歌
…
编号99歌手在唱歌
编号100歌手在唱歌
bingo。