java 并发编程(三)之synchronized


上两个章节: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修饰的不是整个方法而是某一段代码块,因此肯定效率要更有,虽然在在本例中没有体现。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章