ABA问题:
简单来说就是你要操作一个值,当这个值为多少的时候你需要进行什么操作,但是这个值在你操作之前已经被改掉了,又被改回来了。其实你操作的值已经经过了很多操作了。
代码介绍:
AtomicInteger atomicInteger = new AtomicInteger(2022); //底层用的cas
//public final boolean compareAndSet(int expect, int update)
//如果期望的值达到了就更新,否则不更新
//对于写的sql:利用乐观锁解决问题:要知道谁动了线程!
//=======捣乱的线程=========
//=======捣乱的线程=========
new Thread(()->{
System.out.println(atomicInteger.compareAndSet(2022, 2023));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2023, 2022));
System.out.println(atomicInteger.get());
},"A").start();
new Thread(()->{
System.out.println(atomicInteger.compareAndSet(2022, 6666));
System.out.println(atomicInteger.get());
},"B").start();
解决办法:加版本号与乐观锁的实现机制差不多
AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号
System.out.println("a1=>"+stamp);
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//下面这一段代码跟乐观锁里面的version+1版本号操作一样
System.out.println(atomicInteger.compareAndSet(1, 2,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a2=>"+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(2, 1,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a3=>"+atomicInteger.getStamp());
},"a").start();
//与乐观锁的原理相同,由于中间修改过了线程,导致正常的线程不能修改,返回false
new Thread(()->{
int stamp = atomicInteger.getStamp();//版本号获取
System.out.println("b1=>"+stamp);
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(atomicInteger.compareAndSet(1, 6, stamp, stamp + 1));
System.out.println("b2=>"+atomicInteger.getStamp());
},"b").start();
这里需要注意的一点就是Junit测试类线程执行睡眠sleep()后次线程后面的程序不能进行;因为junit执行的程序必须是激活状态的。而sleep是睡眠状态,一旦执行就会自动退出程序。 所以在测试类里使用sleep()等方法是不会生效的。
测试类注意点:测试类的包名需要和启动类保持一致,否则会扫描不到bean
LockSupport.park()会使线程进入到什么状态?
public static void main(String[] args) throws Exception {
Thread parkThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入park");
LockSupport.park(this);
System.out.println(Thread.currentThread().getName() + "解除park");
}
}, "parkThread");
parkThread.start();
}
我们使用jps来查看目前系统正在运行的线程号:
然后再使用istack命令来查看线程的状态,当然这个线程的状态指的是jvm中的状态,而不是操作系统中的状态。
我们可以看到状态是waiting ,在操作系统中就是sleep状态。 线程id为0X3008,这是16进制的,转换成10进制,就可以通过这个id查看状态了。
通过两种方式可以解除:
第一种:LockSupport.unpark();
Thread unParkThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入unPark");
LockSupport.unpark(parkThread);
System.out.println(Thread.currentThread().getName() + "退出unPark");
}
}, "unParkThread");
unParkThread.start();
第二种: thread.interrupt()
Thread unParkThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入中断");
parkThread.interrupt();
System.out.println(Thread.currentThread().getName() + "退出中断");
}
}, "unParkThread");
unParkThread.start();
关闭端口命令:
查询端口的进程id:
netstat -ano |findstr 8084
杀死进程:
taskkill /t /f /pid 29620