欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多线程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关键字包裹的模块尽量小,同步花费的时间就短。