多线程synchronized关键字与局部变量,全局变量,静态变量
程序员文章站
2022-03-24 08:30:04
...
问题描述:
观察局部变量,全局变量,静态变量在单线程和多线程环境下的不同表现,为多线程编程提供思路。
我们看一个简单例子,它的业务可以理解成售票功能。有一个静态变量记录总票数。
public class MultiThread {
//我的电脑,这个值为4
private static final int core = Runtime.getRuntime().availableProcessors();
//线程池
ExecutorService es = Executors.newCachedThreadPool();
public void testBooking(){
ThreadService threadService = new ThreadService();
//线程池新起一个线程
//里面放入runnable类,该类的run方法是执行订票方法。
es.submit(()->threadService.bookTicket());
}
public static void main(String[] args) {
MultiThread mt = new MultiThread();
mt.testBooking();
mt.es.shutdown();
}
}
上面定义了一个测试类,testBooking方法里通过线程池创建一个新城,执行订票方法。
public class ThreadService {
//静态变量,总票数
private static int static_count = 1000;
//全局变量
private int out_count = 0;
public void bookTicket(){
//局部变量
int count =0;
//有票可出售,就继续售票
while (ThreadService.static_count>0){
if(ThreadService.static_count<=0){
break;
}
//卖出一张票,全局变量递增
out_count++;
//卖出一张票,局部变量递增
count++;
//扣除余票
ThreadService.static_count--;
try {
//模拟出票后,售票员跟顾客说两句话,需要花费1毫秒时间
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(ThreadService.static_count==0){
System.out.println("线程:" + Thread.currentThread().getName() + ",卖出了票数:"+ count+", out_count="+out_count);
}
}
}
上面的程序调用一个线程运行,其打印结果如下:
线程:pool-1-thread-1,卖出了票数:1000, out_count=1000
可以看到,静态变量被设置成1000,静态变量每次递减1,局部变量和全局变量就每次递增1,而局部变量和全局变量最后的值都是1000,符合我们的预期。
我们把该程序改成多线程运行,但没对它进行同步操作,看看结果是怎样的:
public class MultiThread {
//我的电脑,这个值为4
private static final int core = Runtime.getRuntime().availableProcessors();
//线程池
ExecutorService es = Executors.newCachedThreadPool();
public void testBooking(){
ThreadService threadService = new ThreadService();
//线程池新起4个线程
//里面放入runnable类,该类的run方法是执行订票方法。
es.submit(()->threadService.bookTicket());
es.submit(()->threadService.bookTicket());
es.submit(()->threadService.bookTicket());
es.submit(()->threadService.bookTicket());
}
public static void main(String[] args) {
MultiThread mt = new MultiThread();
mt.testBooking();
mt.es.shutdown();
}
}
而ThreadService类没做任何改变:
public class ThreadService {
//静态变量,总票数
private static int static_count = 1000;
//全局变量
private int out_count = 0;
public void bookTicket(){
//局部变量
int count =0;
//有票可出售,就继续售票
while (ThreadService.static_count>0){
if(ThreadService.static_count<=0){
break;
}
//卖出一张票,全局变量递增
out_count++;
//卖出一张票,局部变量递增
count++;
//扣除余票
ThreadService.static_count--;
try {
//模拟出票后,售票员跟顾客说两句话,需要花费1毫秒时间
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(ThreadService.static_count==0){
System.out.println("线程:" + Thread.currentThread().getName() + ",卖出了票数:"+ count+", out_count="+out_count);
}
}
}
看下运行Main方法,每次运行得到的结果都不相同,
第一次运行的结果:
线程:pool-1-thread-4,卖出了票数:339, out_count=998
线程:pool-1-thread-1,卖出了票数:337, out_count=998
线程:pool-1-thread-2,卖出了票数:344, out_count=998
线程:pool-1-thread-3,卖出了票数:338, out_count=998
第二次运行的结果:
线程:pool-1-thread-4,卖出了票数:300, out_count=998
线程:pool-1-thread-1,卖出了票数:313, out_count=998
线程:pool-1-thread-2,卖出了票数:298, out_count=998
线程:pool-1-thread-3,卖出了票数:299, out_count=998
但相同的是,4个线程卖出去的票数相加都超过了总票数1000,原因在于每个线程拿到的 ThreadService.static_count 变量是过时的,每个线程正对它+1的时候,其他线程已经对它进行了+1操作。
这里我们对这个+1操作进行同步,确保每次获取值并+1是一次原子操作。
MultiThread 类保持4个线程不变,以下是修改后的ThreadService 类:
public class ThreadService {
static ThreadService instance = new ThreadService();
//静态变量,总票数
private static int static_count = 1000;
//全局变量
private int out_count = 0;
public void bookTicket(){
//局部变量
int count =0;
//有票可出售,就继续售票
while (ThreadService.static_count>0){
//同步块,对给定的实例对象进行加锁,
//每次线程进入被synchronized包裹的代码段,都会要求请求instance实例的锁。
synchronized(instance){
if(ThreadService.static_count<=0){
break;
}
//卖出一张票,全局变量递增
out_count++;
//卖出一张票,局部变量递增
count++;
//扣除余票
ThreadService.static_count--;
}
try {
//模拟出票后,售票员跟顾客说两句话,需要花费1毫秒时间
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(ThreadService.static_count==0){
System.out.println("线程:" + Thread.currentThread().getName() + ",卖出了票数:"+ count+", out_count="+out_count);
}
}
}
运行Main方法,得到结果如下:
线程:pool-1-thread-2,卖出了票数:250, out_count=1000
线程:pool-1-thread-1,卖出了票数:253, out_count=1000
线程:pool-1-thread-3,卖出了票数:249, out_count=1000
线程:pool-1-thread-4,卖出了票数:248, out_count=1000
四个线程卖出的票数相加正好是1000,全局变量也是1000.
总结:
1.方法内的局部变量不受多线程影响。
2.使用多线程操作静态变量时,要对静态变量进行原子操作。
3.synchronized关键字包裹的模块尽量小,同步花费的时间就短。