在类中使用相互独立的属性同步
当使用synchronized关键字去同步一个代码块时,我们必须传递一个引用,通常情况下可以使用this关键字作为参数,但是也可以使用其他的引用;例如在一个类中有两个属性,当多个线程共享这个类时,必须同步这两个属性;在同一时间,一个线程访问其中一个属性,另外一个线程访问另外一个属性,这样也是没问题的;
在下面的例子中模拟一个电影院,这个电影院有两个荧幕和两个独立的售票窗口,每个售票窗口对应一个荧幕;相互不影响;所以它们余票数目也是相互独立的;
动手实验
1.创建一个cinema,并分别实现两套相互独立的售票和返票的方法
public class Cinema {
private long vacanciesCinema1;
private long vacanciesCinema2;
private final Object controlCinema1,controlCinema2;
public Cinema(){
controlCinema1=new Object();
controlCinema2=new Object();
vacanciesCinema1=20;
vacanciesCinema2=20;
}
// It uses the controlCinema1 object to control the access to
// the synchronized block of code
public boolean sellTickets1 (int number) {
synchronized (controlCinema1) {
if (number<vacanciesCinema1) {
vacanciesCinema1-=number;
return true;
} else {
return false;
}
}
}
// It uses the controlCinema2 object to control the access to
// the synchronized block of code
public boolean sellTickets2 (int number){
synchronized (controlCinema2) {
if (number<vacanciesCinema2) {
vacanciesCinema2-=number;
return true;
} else {
return false;
}
}
}
// It uses the controlCinema1 object to control the access to
// the synchronized block of code
public boolean returnTickets1 (int number) {
synchronized (controlCinema1) {
vacanciesCinema1+=number;
return true;
}
}
// It uses the controlCinema2 object to control the access to
// the synchronized block of code
public boolean returnTickets2 (int number) {
synchronized (controlCinema2) {
vacanciesCinema2+=number;
return true;
}
}
public long getVacanciesCinema1() {
return vacanciesCinema1;
}
public long getVacanciesCinema2() {
return vacanciesCinema2;
}
}
2.创建两个售票窗
(1)
public class TicketOffice1 implements Runnable {
private Cinema cinema;
public TicketOffice1 (Cinema cinema) {
this.cinema=cinema;
}
@Override
public void run() {
cinema.sellTickets1(3);
cinema.sellTickets1(2);
cinema.sellTickets2(2);
cinema.returnTickets1(3);
cinema.sellTickets1(5);
cinema.sellTickets2(2);
cinema.sellTickets2(2);
cinema.sellTickets2(2);
}
}
(2)
public class TicketOffice2 implements Runnable {
private Cinema cinema;
public TicketOffice2(Cinema cinema) {
this.cinema=cinema;
}
@Override
public void run() {
cinema.sellTickets2(2);
cinema.sellTickets2(4);
cinema.sellTickets1(2);
cinema.sellTickets1(1);
cinema.returnTickets2(2);
cinema.sellTickets1(3);
cinema.sellTickets2(2);
cinema.sellTickets1(2);
}
}
3.Main方法
public class Main {
public static void main(String[] args) {
Cinema cinema=new Cinema();
TicketOffice1 ticketOffice1=new TicketOffice1(cinema);
Thread thread1=new Thread(ticketOffice1,"TicketOffice1");
TicketOffice2 ticketOffice2=new TicketOffice2(cinema);
Thread thread2=new Thread(ticketOffice2,"TicketOffice2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Room 1 Vacancies: %d\n",cinema.getVacanciesCinema1());
System.out.printf("Room 2 Vacancies: %d\n",cinema.getVacanciesCinema2());
}
}
运行结果:
Room 1 Vacancies: 5
Room 2 Vacancies: 6
要点
JVM确保一个线程只能访问其中一个被同步的同一个对象的代码块(这里是说的是对象,而不是类),在这个例子中,vacanciesCinema1,vacanciesCinema2这两个对象控制访问对应的属性,所以每次只能有一个线程修改一个属性;两个线程也可以同时运行,分别修改对应的属性;