java 多线程全局变量_Java多线程操作局部变量与全局变量

本文深入探讨了Java多线程中全局变量和局部变量的同步问题,通过示例解释了为何需要线程同步,并介绍了三种解决共享变量问题的方法:多对象多线程、局部变量和ThreadLocal。此外,还讲解了如何通过synchronized关键字控制执行步骤,以及如何构建线程池、使用Callable和CompletionService处理返回值,以及实现生产者-消费者模型和信号量控制。
摘要由CSDN通过智能技术生成

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

为什么要线程同步?

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

共享变量

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

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyRunner implementsRunnable2 {3 public int sum = 0;4

5 public voidrun()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() throwsInterruptedException24 {25 MyRunner runner = newMyRunner();26 Thread thread1 = newThread(runner);27 Thread thread2 = newThread(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:步骤12 线程1:步骤23 线程1:步骤34 线程2:步骤15 线程2:步骤26 线程2:步骤3

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

线程1:步骤1

线程2:步骤1

线程1:步骤2

线程2:步骤2

线程1:步骤3

线程2:步骤3

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

961ddebeb323a10fe0623af514929fc1.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyNonSyncRunner implementsRunnable2 {3 public voidrun() {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() throwsInterruptedException23 {24 MyNonSyncRunner runner = newMyNonSyncRunner();25 Thread thread1 = newThread(runner);26 Thread thread2 = newThread(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() throwsInterruptedException2 {3 Thread thread1 = new Thread(newMyRunner());4 Thread thread2 = new Thread(newMyRunner());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 implementsRunnable2 {3 public voidrun()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() throwsInterruptedException23 {24 MyRunner2 runner = newMyRunner2();25 Thread thread1 = newThread(runner);26 Thread thread2 = newThread(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 implementsRunnable2 {3 public ThreadLocal tl = new ThreadLocal();4

5 public voidrun()6 {7 System.out.println(Thread.currentT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值