单例类:
public class ShareData {
private static ShareData shareData = new ShareData();
// 不安全的线程共享变量
private int x = 0;
private ShareData() {
}
public static ShareData getInstantce() {
return shareData;
}
public synchronized void addX() {
System.out.println(Thread.currentThread().getName() + "in");
x++;
System.out.println("x:" + x);
System.out.println(Thread.currentThread().getName() + "out");
}
public synchronized void subX() {
System.out.println(Thread.currentThread().getName() + "in");
x--;
System.out.println("x:" + x);
System.out.println(Thread.currentThread().getName() + "in");
}
}
模拟请求线程类:
public class OneRequestThread extends Thread {
private static final ShareData shareData = ShareData.getInstantce();
private final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private String id;
private CountDownLatch latch;
public OneRequestThread(String id, CountDownLatch latch) {
super();
this.id = id;
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println(id + "waitting");
latch.await(); // 一直阻塞当前线程,直到计时器的值为0
} catch (InterruptedException e) {
e.printStackTrace();
}
doPost();// 发送post 请求
}
private void doPost() {
System.out.println("模拟用户: " + id + " 模拟请求开始 at " + sdf.format(new Date()));
shareData.addX();
System.out.println("模拟用户: " + id + " 模拟请求结束 at " + sdf.format(new Date()));
}
}
主流程:
public class ThreadTest {
private final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
OneRequestThread threadLog = new OneRequestThread("thread" + i, latch);
threadLog.start();
}
// 计数器減一 所有线程释放 并发访问。
latch.countDown();
System.out.println("所有模拟请求结束 at " + sdf.format(new Date()));
}
}
结果
thread0waitting
thread1waitting
thread2waitting
thread3waitting
thread4waitting
thread5waitting
thread6waitting
thread7waitting
thread8waitting
thread9waitting
模拟用户: thread9 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread5 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread8 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread1 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread3 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread4 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread0 模拟请求开始 at 2018-09-28 23:00:30
Thread-9in
模拟用户: thread6 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread7 模拟请求开始 at 2018-09-28 23:00:30
模拟用户: thread2 模拟请求开始 at 2018-09-28 23:00:30
所有模拟请求结束 at 2018-09-28 23:00:30
x:1
Thread-9out
Thread-2in
x:2
Thread-2out
模拟用户: thread9 模拟请求结束 at 2018-09-28 23:00:30
模拟用户: thread2 模拟请求结束 at 2018-09-28 23:00:30
Thread-7in
x:3
Thread-7out
Thread-6in
x:4
Thread-6out
模拟用户: thread7 模拟请求结束 at 2018-09-28 23:00:30
模拟用户: thread6 模拟请求结束 at 2018-09-28 23:00:30
Thread-0in
x:5
Thread-0out
Thread-4in
x:6
Thread-4out
模拟用户: thread0 模拟请求结束 at 2018-09-28 23:00:30
模拟用户: thread4 模拟请求结束 at 2018-09-28 23:00:30
Thread-3in
x:7
Thread-3out
Thread-1in
x:8
Thread-1out
模拟用户: thread3 模拟请求结束 at 2018-09-28 23:00:30
模拟用户: thread1 模拟请求结束 at 2018-09-28 23:00:30
Thread-8in
x:9
Thread-8out
Thread-5in
x:10
Thread-5out
模拟用户: thread8 模拟请求结束 at 2018-09-28 23:00:30
模拟用户: thread5 模拟请求结束 at 2018-09-28 23:00:30
线程共享变量不安全的原因?
单例ShareData在内存堆中只会开辟一份存储空间,其私有变量x被所有线程共享。方法是原子性的,每一个线程进入同一个方法都会开辟一个栈帧,不同的线程在不同栈帧中操作共享变量导致共享变量的值不可预期,例如明明进入栈帧时候是1,方法还没走完就变成了10,所有在这些单例方法中调用私有变量是不安全的。
synchronized在方法上的作用?
多个线程竞争同一个方法,同一时间只会有一个线程进入某个同步方法,只有当前线程从方法出来了,下一个线程才能去访问,这样避免了单例共享变量被多个线程同时改变而出现线程安全的问题,若想用私有变量,但是又怕并发带来的问题,操作私有变量加synchronized也是一个方案。解决问题的同时synchronized一次只能由一个线程操作的模式也让性能大打折扣。
springmvc&struct2
面试的时候,面试官经常会问相关知识:struct2是原型的,所以它的私有变量是线程安全的。springmvc由spring容器管理的类,默认都是单例的,所以其类的私有变量是线程不安全的。我们常用的HashMap,ArrayList等工具类基本都是线程不安全的。为什么?我们平常基本都不会考虑这些安全不安全,例如在springmvc的controller方法,service方法中(只要你不操线程共享的私有变量和静态共享变量)用这些线程不安全的工具类,根本不需要考虑这些方法的线程安全性。因为方法是原子的,是线程私有的,是安全的,所以在这些方法,根本不需要考虑这些工具类本身的线程安全性,对于性能方面线程不安全的工具类效率也会高于线程安全的类。
springmvc如何规避线程安全问题?
慎用线程共享的私有变量,慎用静态变量。
如何模拟多线程并发请求?
借阅ThreadTest类的实现方案。
CountDownLatch latch = new CountDownLatch(1);
// 计数器減一 所有线程释放 并发访问。
latch.countDown();
动力节点在线课程涵盖零基础入门,高级进阶,在职提升三大主力内容,覆盖Java从入门到就业提升的全体系学习内容。全部Java视频教程免费观看,相关学习资料免费下载!对于火爆技术,每周一定时更新!如果想了解更多相关技术,可以到动力节点在线免费观看Java高并发视频教程哦!
代码小兵64503-29 11:46
代码小兵87208-06 11:36
代码小兵34507-29 13:19