java 线程 全局变量_【转】Java多线程操作局部变量与全局变量

本文介绍了Java线程同步的原因和解决方案,包括共享变量问题和执行步骤同步。通过示例代码展示了如何通过修改运行模型、使用局部变量和ThreadLocal来避免全局变量冲突。此外,还探讨了synchronized关键字在控制执行步骤中的应用,并介绍了并发工具包中的BlockingQueue、Semaphore和CyclicBarrier、CountDownLatch等工具在控制线程执行步骤和线程池同步中的使用方法。
摘要由CSDN通过智能技术生成

原文网址:http://blog.csdn.net/undoner/article/details/12849661

在这篇文章里,我们首先阐述什么是同步,不同步有什么问题,然后讨论可以采取哪些措施控制同步,接下来我们会仿照回顾网络通信时那样,构建一个服务器端的“线程池”,JDK为我们提供了一个很大的concurrent工具包,最后我们会对里面的内容进行探索。

为什么要线程同步?

说到线程同步,大部分情况下, 我们是在针对“单对象多线程”的情况进行讨论,一般会将其分成两部分,一部分是关于“共享变量”,一部分关于“执行步骤”。

共享变量

当我们在线程对象(Runnable)中定义了全局变量,run方法会修改该变量时,如果有多个线程同时使用该线程对象,那么就会造成全局变量的值被同时修改,造成错误。我们来看下面的代码:

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyRunner implements Runnable

2 {

3 public int sum = 0;

4

5 public void run()

6 {

7 System.out.println(Thread.currentThread().getName() + " Start.");

8 for (int i = 1; i <= 100; i++)

9 {

10 sum += i;

11 }

12 try {

13 Thread.sleep(500);

14 } catch (InterruptedException e) {

15 e.printStackTrace();

16 }

17 System.out.println(Thread.currentThread().getName() + " --- The value of sum is " + sum);

18 System.out.println(Thread.currentThread().getName() + " End.");

19 }

20 }

21

22

23 private static void sharedVaribleTest() throws InterruptedException

24 {

25 MyRunner runner = new MyRunner();

26 Thread thread1 = new Thread(runner);

27 Thread thread2 = new Thread(runner);

28 thread1.setDaemon(true);

29 thread2.setDaemon(true);

30 thread1.start();

31 thread2.start();

32 thread1.join();

33 thread2.join();

34 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

这个示例中,线程用来计算1到100的和是多少,我们知道正确结果是5050(好像是高斯小时候玩过这个?),但是上述程序返回的结果是10100,原因是两个线程同时对sum进行操作。

执行步骤

我们在多个线程运行时,可能需要某些操作合在一起作为“原子操作”,即在这些操作可以看做是“单线程”的,例如我们可能希望输出结果的样子是这样的:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 线程1:步骤1

2 线程1:步骤2

3 线程1:步骤3

4 线程2:步骤1

5 线程2:步骤2

6 线程2:步骤3

48304ba5e6f9fe08f3fa1abda7d326ab.png

如果同步控制不好,出来的样子可能是这样的:

线程1:步骤1

线程2:步骤1

线程1:步骤2

线程2:步骤2

线程1:步骤3

线程2:步骤3

这里我们也给出一个示例代码:

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyNonSyncRunner implements Runnable

2 {

3 public void run() {

4 System.out.println(Thread.currentThread().getName() + " Start.");

5 for(int i = 1; i <= 5; i++)

6 {

7 System.out.println(Thread.currentThread().getName() + " Running step " + i);

8 try

9 {

10 Thread.sleep(50);

11 }

12 catch(InterruptedException ex)

13 {

14 ex.printStackTrace();

15 }

16 }

17 System.out.println(Thread.currentThread().getName() + " End.");

18 }

19 }

20

21

22 private static void syncTest() throws InterruptedException

23 {

24 MyNonSyncRunner runner = new MyNonSyncRunner();

25 Thread thread1 = new Thread(runner);

26 Thread thread2 = new Thread(runner);

27 thread1.setDaemon(true);

28 thread2.setDaemon(true);

29 thread1.start();

30 thread2.start();

31 thread1.join();

32 thread2.join();

33 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

如何控制线程同步

既然线程同步有上述问题,那么我们应该如何去解决呢?针对不同原因造成的同步问题,我们可以采取不同的策略。

控制共享变量

我们可以采取3种方式来控制共享变量。

将“单对象多线程”修改成“多对象多线程”

上文提及,同步问题一般发生在“单对象多线程”的场景中,那么最简单的处理方式就是将运行模型修改成“多对象多线程”的样子,针对上面示例中的同步问题,修改后的代码如下:

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 private static void sharedVaribleTest2() throws InterruptedException

2 {

3 Thread thread1 = new Thread(new MyRunner());

4 Thread thread2 = new Thread(new MyRunner());

5 thread1.setDaemon(true);

6 thread2.setDaemon(true);

7 thread1.start();

8 thread2.start();

9 thread1.join();

10 thread2.join();

11 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

我们可以看到,上述代码中两个线程使用了两个不同的Runnable实例,它们在运行过程中,就不会去访问同一个全局变量。

将“全局变量”降级为“局部变量”

既然是共享变量造成的问题,那么我们可以将共享变量改为“不共享”,即将其修改为局部变量。这样也可以解决问题,同样针对上面的示例,这种解决方式的代码如下:

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyRunner2 implements Runnable

2 {

3 public void run()

4 {

5 System.out.println(Thread.currentThread().getName() + " Start.");

6 int sum = 0;

7 for (int i = 1; i <= 100; i++)

8 {

9 sum += i;

10 }

11 try {

12 Thread.sleep(500);

13 } catch (InterruptedException e) {

14 e.printStackTrace();

15 }

16 System.out.println(Thread.currentThread().getName() + " --- The value of sum is " + sum);

17 System.out.println(Thread.currentThread().getName() + " End.");

18 }

19 }

20

21

22 private static void sharedVaribleTest3() throws InterruptedException

23 {

24 MyRunner2 runner = new MyRunner2();

25 Thread thread1 = new Thread(runner);

26 Thread thread2 = new Thread(runner);

27 thread1.setDaemon(true);

28 thread2.setDaemon(true);

29 thread1.start();

30 thread2.start();

31 thread1.join();

32 thread2.join();

33 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

我们可以看出,sum变量已经由全局变量变为run方法内部的局部变量了。

使用ThreadLocal机制

ThreadLocal是JDK引入的一种机制,它用于解决线程间共享变量,使用ThreadLocal声明的变量,即使在线程中属于全局变量,针对每个线程来讲,这个变量也是独立的。

我们可以用这种方式来改造上面的代码,如下所示:

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyRunner3 implements Runnable

2 {

3 public ThreadLocal tl = new ThreadLocal();

4

5 public void run()

6 {

7 System.out.println(Thread.currentThread().getName() + " Start.");

8 for (int i = 0; i <= 100; i++)

9 {

10 if (tl.get() == null)

11 {

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值