线程同步的三种方案

以电影院售票为例

方案一:同步代码块

public class Cinerma {
	int tickets = 50;//剩余票数
	Object lock = new Object();//可以看做钥匙对象
//卖票函数
 public void  sellTicket(int num,int id) throws InterruptedException {//本函数一般跑在子线程中,所以在执行tickets = tickets -num;时会发生线程危险
		
		
	if(tickets>=num){
            System.out.println(Thread.currentThread().getName()+id+":啊哈,有票,让我来跟售票员墨迹一会");//注意此处子线程的id
            Thread.sleep(200);
            System.out.println(Thread.currentThread().getName()+id+":墨迹完了,我要买票了");
            synchronized(lock){//正式买票,同步起来
                
                if(tickets>=num){
                    Thread.sleep(200);
                    tickets = tickets -num;
                    System.out.println(Thread.currentThread().getName()+id+":买了"+num+"张,剩余票数"+tickets);
                }else{
                    System.out.println(Thread.currentThread().getName()+id+":fuck=fuck=fuck=fuck=fuck,刚刚还有票的");
                }
            
            }
        }else{
            System.out.println("没票了咱走吧");
        }
        


方案二:使用加锁对象,ReentrantLock


        ReentrantLock reentrantLock = new ReentrantLock();//放在成员里就好
	//询问处的函数,一次只能一个线程来问,都排队
	public void question(int id) throws InterruptedException{//运行在子线程中的方法
		reentrantLock.lock();//此处开始同步
		System.out.println(Thread.currentThread().getName()+id+":来买爆米花了");
		Thread.sleep(3000);
		System.out.println(Thread.currentThread().getName()+id+":买完了");
		reentrantLock.unlock();//此处停止同步
	}

调用方法:

Cinerma cinerma = new Cinerma();
		
		Thread buyticketThread1 = new BuyTicketThread("情侣", 2, cinerma);//thread的名字,买票数,电影院对象
		Thread buyticketThread2 = new BuyTicketThread("单身狗", 1, cinerma);
		Thread askThread = new AskQuestionThread("爆米花",cinerma);
		
		buyticketThread1.start();
		buyticketThread2.start();
		askThread.start();

class BuyTicketThread extends Thread{//买票线程
	
	int num =0;
	Cinerma cinerma;
 	public BuyTicketThread(String name,int num,Cinerma cinerma){
 		this.num = num;
		 this.cinerma = cinerma;
		 this.setName(name);//设置线程名
	 }
	 
        @Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			for (int i = 0; i < 1000; i++) {//买1000张票
				cinerma.sellTicket(num,i);
				
			}
		} catch (InterruptedException e) {
			 // TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

public class AskQuestionThread extends Thread{//咨询线程
    
    Cinerma cinerma ;
    String name;
    public AskQuestionThread(String name,Cinerma cinerma){
        this.name = name;
        this.cinerma = cinerma;
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 3; i++) {
            try {
                cinerma.question(i);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }

}



 
 

方案3:notify()和wake()

建立一个场景,一位母亲有三个孩子,需要给他们做饭,一次只能做一个,每个孩子都要不停的吃饭,做饭和吃饭都需要时间,循环往复

首先建立一个厨房对象,这个对象是最主要的,里面有做饭和吃饭两个方法,重点是,这两个方法都要用synchronized修饰

public class Kitchen {
	int food = 0;//剩余的饭
//做饭函数
 public synchronized void cook() throws InterruptedException{//注意这个函数被synchronized给修饰了 
		if(food>0){//如果当前还有饭
			
			wait();//妈妈线程等待,等待孩子线程notify她,函数会停留在这一句不动,直到有notify
                        //如果被notify了,则执行下面的
                        food = food +1;//做饭
			System.out.println("做完饭后一共剩余"+food);
			System.out.println(Thread.currentThread().getName()+":饭做好了");
			notifyAll();//呼叫孩子线程
			
			
		}else{//如果当前没剩饭了
			food = food +1;
			System.out.println(food);
			System.out.println(Thread.currentThread().getName()+":饭做好了");
			notifyAll();//通知孩子线程
			
		}
<pre name="code" class="java">                System.out.println("通知完孩子后剩余"+food);

 }//吃饭函数public synchronized void eat() throws InterruptedException {//在子线程中调用if(food == 0){wait();//孩子线程等待,等妈妈线程唤醒他,此时函数会停留在这一句
//如果这个时候被notify了
 food -=1;//吃饭 System.out.println(Thread.currentThread().getName()+"抢到了");notifyAll();//通知她妈没饭了System.out.println(food);}else{food -=1;//吃饭System.out.println(food);System.out.println(Thread.currentThread().getName()+"抢到了");notifyAll();//通知她妈System.out.println(food);}}}

 使用四个线程,包含一个妈妈线程三个孩子线程,不停的调用kitchen里的方法,如下 
 

Kitchen kitchen = new Kitchen();
		Thread motherThread = new Mother(kitchen);
		Thread chilThread1 = new Childrens("大娃", kitchen);
		Thread chilThread2 = new Childrens("二娃", kitchen);
		Thread chilThread3 = new Childrens("三娃", kitchen);
		
		motherThread.start();
		chilThread1.start();
		chilThread2.start();
		chilThread3.start();

//下面是三个线程的定义
public class Childrens extends Thread{
    Kitchen kitchen;
    public Childrens(String name,Kitchen kitchen){
        this.kitchen = kitchen;
        this.setName(name);
        
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 3; i++) {
            try {
                kitchen.eat();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"吃饱了");
        
        
    }
}
public class Mother extends Thread{
    Kitchen kitchen;
    
    public Mother(Kitchen kitchen){
        this.kitchen = kitchen;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            
        
        try {
            kitchen.cook();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }

    }

}



你可能感兴趣的:(线程同步的三种方案)