上两个章节:java 并发编程(一)之synchronized、java 并发编程(二)之synchronized实例
中讲解了synchronized关键字对同步的控制以及演示了在修饰方法的例子。
我们会发现添加synchronized关键字的方法能够完成多个线程对于同一个方法的同步,但显然不是很好的实现,因为首要
问题就是“效率”,很明显,我们采用高并发设计程序目的就是最大限度的利用cpu的时间片来达到高效的目的,然而在引入
synchronized后,所有线程对于同一方法的访问将是“串行”的。
因此我们在设计“高并发”的时候,如果要采用synchronized机制进行同步,有一个原则:“在保证同步的前提下,临界区(critical section)最小化”
synchronized也可以通过输入“对象”来对多个线程访问同一“代码块”进行同步。通常这个对象是“方法”所在“对象”的引用(this),然而我们也可以
通过传入“非依赖属性”对同步代码块进行同步。
下面我们给出了一个例子:一个电影院有两个售票处和两个放映厅,每个售票处售出的电影票同时只能应用于一个放映厅。因此我们需要对两个售票处
的售票行为进行“同步”。
我们编写了四个类:
Cinema:模拟电影院,vCinema1记录放映厅1的票数,vCinema2记录放映厅2的票数
CinemaOffice1:模拟售票处1,售出放映厅1的电影票
CinemaOffice2 :模拟售票处2,售出放映厅2的电影票
Test:测试类
package com.z.cinema_2;
public class Cinema {
private int vCinema1;
private int vCinema2;
private final Object cinemaController1;
private final Object cinemaController2;
public Cinema(){
vCinema1=20;
vCinema2=20;
cinemaController1 = new Object();
cinemaController2 = new Object();
}
public boolean sellTicket1(int ticketNum){
synchronized(cinemaController1){
if(vCinema1 <=0 || vCinema1 < ticketNum){
return false;
}else{
vCinema1-=ticketNum;
return true;
}
}
}
public boolean sellTicket2(int ticketNum){
synchronized(cinemaController2){
if(vCinema2 >0 && vCinema2 >= ticketNum){
vCinema2-=ticketNum;
return true;
}else return false;
}
}
public void returnTicket1(int ticketNum){
synchronized(cinemaController1){
vCinema1+=ticketNum;
}
}
public void returnTicket2(int ticketNum){
synchronized(cinemaController2){
vCinema2+=ticketNum;
}
}
public int getTicket1(){
return vCinema1;
}
public int getTicket2(){
return vCinema2;
}<pre name="code" class="java">package com.z.cinema_2;
public class CinemaOffice2 implements Runnable {
private Cinema cinema;
public CinemaOffice2(Cinema cinema){
this.cinema = cinema;
}
@Override
public void run() {
cinema.sellTicket2(2);
cinema.sellTicket2(4);
cinema.sellTicket2(6);
cinema.returnTicket2(1);
cinema.returnTicket2(3);
cinema.returnTicket2(5);
}
}
}
package com.z.cinema_2;
public class CinemaOffice1 implements Runnable{
private Cinema cinema;
public CinemaOffice1(Cinema cinema){
this.cinema = cinema;
}
public void run(){
cinema.sellTicket1(1);
cinema.sellTicket1(3);
cinema.sellTicket1(5);
cinema.returnTicket1(2);
cinema.returnTicket1(4);
cinema.returnTicket1(6);
}
}
package com.z.cinema_2;
public class Test {
public static void main(String[] args){
Cinema cinema = new Cinema();
Thread cinemaOffice1Thread = new Thread(new CinemaOffice1(cinema));
Thread cinemaOffice2Thread = new Thread(new CinemaOffice2(cinema));
cinemaOffice1Thread.start();
cinemaOffice2Thread.start();
try {
cinemaOffice1Thread.join();
cinemaOffice2Thread.join();
} catch (InterruptedException e) {
System.err.println(e);
}
System.out.println(""
+ "Cinema2 余票:"
+ cinema.getTicket1()
+ "\r"
+ "Cinema1 余票:"
+ cinema.getTicket2());
}
}
运行结果:
Cinema1 余票:23
Cinema2 余票:17
在Cinema中,我们通过两个“非依赖属性“同步了两类操作,一类是对Ticket1的发放和退票操作,一类是对Ticket2的发放和退票操作。
通过这两个”非依赖属性“完成对这两种操作的同步。
同时我们比较之前的例子,因为此时synchronized修饰的不是整个方法而是某一段代码块,因此肯定效率要更有,虽然在在本例中没有体现。