java 同步鎖 synchronized 死鎖 lock鎖 jion 線程結束

多個線程在操做共享的數據且操做共享數據的線程代碼有多條。當一個線程在執行操做共享數據的多條代碼過程當中,其餘線程參與了運算。就會致使線程安全問題的產生。java

同步代碼塊原理:將多條操做共享數據的線程代碼封裝起來,當有線程在執行這些代碼的時候,其餘線程時不能夠參與運算的。必需要當前線程把這些代碼都執行完畢後,其餘線程才能夠參與運算。 安全

同步的弊端:相對下降了效率,由於同步外的線程的都會判斷同步鎖。jvm

同步的前提:同步中必須有多個線程並使用同一個鎖。ide

class Single
{
	private static Single s = null;
	private Single(){}
	
	public static Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)	 //這個用同一對象鎖 只能建立一個對象	
			{
				if(s==null)
					s = new Single();
			}
		}
		return s;
	}
}

同步鎖分爲同步代碼塊鎖、同步函數鎖,同步函數的使用的鎖是this;同步代碼塊的鎖是任意的對象。使用最多的通常是同步代碼塊。函數

使用wait、notify、notifylAlll與同步鎖配合的應用,wait()將線程添加到線程池中。notify釋放本對象線程池中的任意一個線程,notifyAll釋放本線程池中全部的線程this

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
	public synchronized void set(String name)//  
	{
		while(flag)   //喚醒時 從新判斷flag  防止flag不符合也往下運行 
			try{this.wait();}catch(InterruptedException e){}  //在這裏喚醒 醒後上面判斷flag
		
		this.name = name + count;
		count++;
		System.out.println(Thread.currentThread().getName()+"."+this.name);
		flag = true;
		notifyAll();   //喚醒全部進程 防止 出現死鎖 可是 喚醒全部鎖 致使從新判斷 效率降低了
	}

	public synchronized void out()
	{
		while(!flag)
			try{this.wait();}catch(InterruptedException e){}	
		System.out.println(Thread.currentThread().getName()+".........."+this.name);
		flag = false;
		notifyAll();
	}
}

class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("here");
		}
	}
}

class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}


class  ProducerConsumerDemo
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
		t0.start();
		t1.start();
		t2.start();
		t3.start();

	}
}

同步鎖若是操做很差會帶來新問題:死鎖。spa

線程a、線程b都一直在while(1)中運行,當線程a持有鎖a要調用鎖b時,若是此時cpu進行線程切換換到線程b,線程b運行,線程b持有鎖b要調用鎖a,結果發現,鎖a被線程a持有無法調用就會等待系統調度切換到線程a,線程a繼續運行要調用鎖b,發現鎖b被b線程持有,也無法運行,二者就造成了死鎖。線程

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}

	public void run()
	{
		
		if(flag)
		{
			while(true)
			    synchronized(MyLock.locka)    //同步鎖a
			    {
			        System.out.println(Thread.currentThread().getName()+"1 locka.");
				synchronized(MyLock.lockb){	
				    System.out.println(Thread.currentThread().getName()+"1 lockb..");
				}
			    }
		}
		else
		{
			while(true)			
			    synchronized(MyLock.lockb)    //同步鎖b
		            {
				System.out.println(Thread.currentThread().getName()+"2 lockb");
				synchronized(MyLock.locka)
				{
				    System.out.println(Thread.currentThread().getName()+"2 locka.");
				}
			    }
		}

	}

}

class MyLock     //鎖對象
{
	public static final Object locka = new Object();     //a鎖
	public static final Object lockb = new Object();     //b鎖
}


class DeadLockTest 
{
	public static void main(String[] args) 
	{
		Test a = new Test(true);        
		Test b = new Test(false);       

		Thread t1 = new Thread(a);      //線程a
		Thread t2 = new Thread(b);      //線程b
		t1.start();
		t2.start();
	}
}


因爲synchronized可能會致使死鎖,而防止死鎖的notifyAll釋放全部進程,致使效率降低,java引入lock鎖,Lock接口:替代了同步代碼塊或者同步函數。將同步的隱式鎖操做變成現實鎖操做。code

同時更爲靈活。能夠一個鎖上加上多組監視器。lock():獲取鎖。unlock():釋放鎖,一般須要定義finally代碼塊中。對象

import java.util.concurrent.locks.*;

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;

	Lock lock = new ReentrantLock();   //建立鎖對象

	Condition producer_con = lock.newCondition();  //一把鎖能夠掛多個監視器
	Condition consumer_con = lock.newCondition();

	
	public  void set(String name)
	{
		lock.lock();   //上鎖 
		try
		{
			while(flag)
			try{producer_con.await();}catch(InterruptedException e){}
		
			this.name = name + count;
			count++;
			System.out.println(Thread.currentThread().getName()+"."+this.name);
			flag = true;
			consumer_con.signal();   //激活消費者線程 這樣就能夠提升效率
		}
		finally
		{
			lock.unlock();  //解除鎖
		}
		
	}

	public  void out()
	{
		lock.lock();
		try
		{
			while(!flag)
			try{cousumer_con.await();}catch(InterruptedException e){}	
			System.out.println(Thread.currentThread().getName()+"..."+this.name);
			flag = false;
			producer_con.signal();   //激活生產者線程
		}
		finally
		{
			lock.unlock();
		}
		
	}
}

class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("provide");
		}
	}
}

class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}



class  ProducerConsumerDemo2
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
		t0.start();
		t1.start();
		t2.start();
		t3.start();

	}
}

在lock鎖中,一個鎖能夠添加多個condition監視器,確保能夠對指定線程進行操做,提升了運行效率,await()掛起線程 signal()恢復線程。


wait與sleep區別

1,wait能夠指定時間也能夠不指定。sleep必須指定時間。

2,在同步中時,對cpu的執行權和鎖的處理不一樣。

    wait:釋放執行權,釋放鎖。sleep:釋放執行權,不釋放鎖。


線程終止:控制循環一般就用定義標記來完成。若是線程處於了凍結狀態,沒法讀取標記,

可使用interrupt()方法將線程從凍結狀態強制恢復到運行狀態中來,讓線程具有cpu的執行資格。 當強制動做會發生了InterruptedException,記得要處理

class StopThread implements Runnable
{
	private boolean flag = true;
	public synchronized void run()
	{
		while(flag)   //線程終止 標誌
		{
			try
			{
				wait();
			}
			catch (InterruptedException e)  //interrupt強制喚醒 產生的異常
			{
				System.out.println(Thread.currentThread().getName()+"....."+e);
				flag = false;    
			}
			
			System.out.println(Thread.currentThread().getName()+"......++++");
		}
	}

}



class StopThreadDemo 
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();

		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);

		t1.start();
		t2.setDaemon(true);  //守護線程 若是沒有別的線程 會自動退出
		t2.start();

		int num = 1;
		for(;;)
		{
			if(++num==50)
			{
				t1.interrupt();
//				t2.interrupt();  //若是沒設置爲守護線程 須要這句話來 退出
				break;
			}
			System.out.println("main...."+num);
		}

		System.out.println("over");
	}
}

setDaemon()守護線程,必須在線程前開啓前設置線程爲守護線程,守護線程後臺運行,當沒有前臺線程時,守護線程會自動結束而後結束jvm虛擬機。


jion()函數,線程掛起,直到該線程運行完,掛起此線程的線程才運行。

相關文章
相關標籤/搜索