基本线程同步(三)在同步的类里安排独立属性

简介:

在同步的类里安排独立属性

当你使用synchronized关键字来保护代码块时,你必须通过一个对象的引用作为参数。通常,你将会使用this关键字来引用执行该方法的对象,但是你也可以使用其他对象引用。通常情况下,这些对象被创建只有这个目的。比如,你在一个类中有被多个线程共享的两个独立属性。你必须同步访问每个变量,如果有一个线程访问一个属性和另一个线程在同一时刻访问另一个属性,这是没有问题的。

在这个指南中,你将学习如何解决这种情况的一个例子,编程模拟一家电影院有两个屏幕和两个售票处。当一个售票处出售门票,它们用于两个电影院的其中一个,但不能用于两个,所以在每个电影院的免费席位的数量是独立的属性。

准备工作

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建一个Cinema类,添加两个long类型的属性,命名为vacanciesCinema1和vacanciesCinema2。


1 public class Cinema {
2 private long vacanciesCinema1;
3 private long vacanciesCinema2;

2.给Cinema类添加两个额外的Object属性,命名为controlCinema1和controlCinema2。


1 private final Object controlCinema1, controlCinema2;

3.实现Cinema类的构造方法,初始化所有属性。


1 public Cinema(){
2 controlCinema1=new Object();
3 controlCinema2=new Object();
4 vacanciesCinema1=20;
5 vacanciesCinema2=20;
6 }

4.实现sellTickets1()方法,当第一个电影院出售一些门票将调用它。使用controlCinema1对象来控制访问synchronized的代码块。


01 public boolean sellTickets1 (int number) {
02 synchronized (controlCinema1) {
03 if (number<vacanciesCinema1) {
04 vacanciesCinema1-=number;
05 return true;
06 } else {
07 return false;
08 }
09 }
10 }

5.实现sellTickets2()方法,当第二个电影院出售一些门票将调用它。使用controlCinema2对象来控制访问synchronized的代码块。


01 public boolean sellTickets2 (int number) {
02 synchronized (controlCinema2) {
03 if (number<vacanciesCinema2) {
04 vacanciesCinema2-=number;
05 return true;
06 } else {
07 return false;
08 }
09 }
10 }

6.实现returnTickets1()方法,当第一个电影院被退回一些票时将调用它。使用controlCinema1对象来控制访问synchronized的代码块。


1 public boolean returnTickets1 (int number) {
2 synchronized (controlCinema1) {
3 vacanciesCinema1+=number;
4 return true;
5 }
6 }

7.实现returnTickets2()方法,当第二个电影院被退回一些票时将调用它。使用controlCinema2对象来控制访问synchronized的代码块。


1 public boolean returnTickets2 (int number) {
2 synchronized (controlCinema2) {
3 vacanciesCinema2+=number;
4 return true;
5 }
6 }

8.实现其他两个方法,用来返回每个电影院空缺位置的数量。


1 public long getVacanciesCinema1() {
2 return vacanciesCinema1;
3 }
4 public long getVacanciesCinema2() {
5 return vacanciesCinema2;
6 }

9.实现TicketOffice1类,并指定它实现Runnable接口。


1 public class TicketOffice1 implements Runnable {

10.声明一个Cinema对象,并实现该类(类TicketOffice1)的构造器用来初始化这个对象。


1 private Cinema cinema;
2 public TicketOffice1 (Cinema cinema) {
3 this.cinema=cinema;
4 }

11.实现run()方法,用来模拟在两个电影院的一些操作。


01 @Override
02 public void run() {
03 cinema.sellTickets1(3);
04 cinema.sellTickets1(2);
05 cinema.sellTickets2(2);
06 cinema.returnTickets1(3);
07 cinema.sellTickets1(5);
08 cinema.sellTickets2(2);
09 cinema.sellTickets2(2);
10 cinema.sellTickets2(2);
11 }

12.实现TicketOffice2类,并指定它实现Runnable接口。


1 public class TicketOffice2 implements Runnable {

13.声明一个Cinema对象,并实现该类(类TicketOffice2)的构造器用来初始化这个对象。


1 private Cinema cinema;
2 public TicketOffice2 (Cinema cinema) {
3 this.cinema=cinema;
4 }

14.实现run()方法,用来模拟在两个电影院的一些操作。


01 @Override
02 public void run() {
03 cinema.sellTickets2(2);
04 cinema.sellTickets2(4);
05 cinema.sellTickets1(2);
06 cinema.sellTickets1(1);
07 cinema.returnTickets2(2);
08 cinema.sellTickets1(3);
09 cinema.sellTickets2(2);
10 cinema.sellTickets1(2);
11 }

15.通过创建类名为Main,且包括main()方法来实现这个示例的主类。


1 public class Main {
2 public static void main(String[] args) {

16.声明和创建一个Cinema对象。


1 Cinema cinema=new Cinema();

17.创建一个TicketOffice1对象,并且用线程来运行它。


1 TicketOffice1 ticketOffice1=new TicketOffice1(cinema);
2 Thread thread1=new Thread(ticketOffice1,"TicketOffice1");

18.创建一个TicketOffice2对象,并且用线程来运行它。


1 TicketOffice2 ticketOffice2=new TicketOffice2(cinema);
2 Thread thread2=new Thread(ticketOffice2,"TicketOffice2");

19.启动这两个线程。


1 thread1.start();
2 thread2.start();

20.等待线程执行完成。


1 try {
2 thread1.join();
3 thread2.join();
4 } catch (InterruptedException e) {
5 e.printStackTrace();
6 }

21.两个电影院的空缺数写入控制台。


1 System.out.printf("Room 1 Vacancies: %d\n",cinema.getVacanciesCinema1());
2 System.out.printf("Room 2 Vacancies: %d\n",cinema.getVacanciesCinema2());

它是如何工作的…

当你使用synchronized关键字来保护代码块,你使用一个对象作为参数。JVM可以保证只有一个线程可以访问那个对象保护所有的代码块(请注意,我们总是谈论的对象,而不是类)。

注释:在这个示例中,我们用一个对象来控制vacanciesCinema1属性的访问。所以,在任意时刻,只有一个线程能修改该属性。用另一个对象来控制 vacanciesCinema2属性的访问。所以,在任意时刻,只有一个线程能修改这个属性。但是可能有两个线程同时运行,一个修改 vacancesCinema1属性而另一个修改vacanciesCinema2属性。

当你运行这个示例,你可以看到每个电影院的空缺数量的最后的结果总是预期的。在以下截图中,你可以看到应用程序的执行结果:

3

不止这些…

synchronize关键字还有其他重要用法,请见其他指南中解释这个关键字使用的参见部分。

参见

在第2章,基本线程同步中在同步代码中使用条件的指南。 

目录
相关文章
|
1月前
|
Java 云计算
Java多线程编程中的同步与互斥机制探析
在当今软件开发领域,多线程编程是一项至关重要的技能。本文将深入探讨Java中的同步与互斥机制,分析其在多线程环境下的应用及实现原理,帮助读者更好地理解并运用这一关键技术。
22 4
|
1月前
|
存储 缓存 安全
【C/C++ 关键字 存储类说明符 】 线程局部变量的魔法:C++ 中 thread_local的用法
【C/C++ 关键字 存储类说明符 】 线程局部变量的魔法:C++ 中 thread_local的用法
33 0
|
10天前
|
存储 安全 Java
java多线程之原子操作类
java多线程之原子操作类
|
12天前
|
Java
Java中的多线程实现:使用Thread类与Runnable接口
【4月更文挑战第8天】本文将详细介绍Java中实现多线程的两种方法:使用Thread类和实现Runnable接口。我们将通过实例代码展示如何创建和管理线程,以及如何处理线程同步问题。最后,我们将比较这两种方法的优缺点,以帮助读者在实际开发中选择合适的多线程实现方式。
20 4
|
14天前
|
Java Spring
springboot单类集中定义线程池
该内容是关于Spring中异步任务的配置和使用步骤。首先,在启动类添加`@EnableAsync`注解开启异步支持。然后,自定义线程池类`EventThreadPool`,设置核心和最大线程数、存活时间等参数。接着,将线程池bean注入到Spring中,如`@Bean(&quot;RewardThreadPool&quot;)`。最后,在需要异步执行的方法上使用`@Async`注解,例如在一个定时任务类中,使用`@Scheduled(cron = &quot;...&quot;)`和`@Async`结合实现异步定时任务。
14 2
|
24天前
|
Linux API C++
【C++ 线程包裹类设计】跨平台C++线程包装类:属性设置与平台差异的全面探讨
【C++ 线程包裹类设计】跨平台C++线程包装类:属性设置与平台差异的全面探讨
50 2
|
26天前
|
存储 编解码 算法
【ffmpeg音视频同步】解决ffmpeg音视频中多线程之间的数据同步问题
【ffmpeg音视频同步】解决ffmpeg音视频中多线程之间的数据同步问题
38 2
|
1月前
|
安全 C++ 开发者
【C++多线程同步】C++多线程同步和互斥的关键:std::mutex和相关类的全面使用教程与深度解析
【C++多线程同步】C++多线程同步和互斥的关键:std::mutex和相关类的全面使用教程与深度解析
18 0
|
10天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
22天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3

热门文章

最新文章