synchronized
使用synchronized實現同步有2種方式:
- 同步方法(靜態與非靜態)
- 同步程式碼塊
任何Java物件均可作為鎖使用,其中,使用的鎖物件有以下3種:
- 靜態同步方法中,鎖是當前類的Class物件
- 非靜態同步方法中,鎖是當前物件this(呼叫該方法的物件)
- 同步程式碼塊中,瑣是手動配置的物件
同步方法
private synchronized void f()
{
//...
}
同步程式碼塊
private void f()
{
synchronized(this)
{
//...
}
}
Lock
Lock介面
中規定了鎖必須實現的一些方法
void lock()
void unlock()
boolean tryLock()
boolean tryLock(long time, TimeUnit unit)
Condition newCondition()
Lock介面的實現類有ReentarntLock
和ReentrantReadWriteLock
2種,它們都提供了非公平鎖和公平鎖2種形式。
ReentrantLock
可重入鎖,執行緒可以重複的獲取已經持有的鎖。
鎖中維護著一個持有計數,來追蹤對lock方法的巢狀呼叫,每次lock計數加1,每次unlock計數減1。
只有持有計數為0時,才釋放鎖。
使用示例
private Lock lock=new ReentrantLock();
private void f()
{
lock.lock();
try
{
// ...
}
finally
{
lock.unlock();
}
}
公平鎖,執行緒排程器將優先(並不保證一定)執行等待時間最長的執行緒。
在建立ReentrantLock時,可以傳入Boolean引數true,建立一個公平鎖。
Lock fairLock=new ReentrantLock(true);
除了使用lock()
獲取鎖外,還可以使用tryLock()
嘗試獲取鎖,如果成功,返回true,否則,返回false,並且執行緒可以立即離開去做其它事情。
還可以呼叫傳入超時引數。
ReentrantReadWriteLock
可重入讀寫鎖。如果對一個數據結構進行讀操作的次數遠遠大於寫操作的次數,就可以使用讀寫鎖提高效能。
使用示例
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();// 建立讀寫鎖
private Lock readLock=lock.readLock();// 讀鎖
private Lock writeLock=lock.writeLock();// 寫鎖
// 讀操作加讀鎖
public void read()
{
readLock.lock();
try
{
// ...
}
finally
{
readLock.unlock();
}
}
// 寫操作加寫鎖
public void write()
{
writeLock.lock();
try
{
// ...
}
finally
{
writeLock.unlock();
}
}
死鎖
死鎖問題的產生,是由於執行緒A和執行緒B互相持有對方需要獲取的鎖物件導致的
private static final Object lockA=new Object();
private static final Object lockB=new Object();
private void deadLock()
{
Thread t1=new Thread(()->{
synchronized(lockA)
{
sleep(1L);
synchronized(lockB)
{
System.out.println("thread 1");
}
}
});
Thread t2=new Thread(()->{
synchronized(lockB)
{
sleep(1L);
synchronized(lockA)
{
System.out.println("thread 2");
}
}
});
t1.start();
t2.start();
}
private void sleep(long timeout)
{
try
{
TimeUnit.SECONDS.sleep(timeout);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
死鎖問題一旦發生,程式就會掛起。所以,必須避免死鎖的發生,有以下幾個方式
- 避免在一個執行緒中同時獲取多個鎖
- 嘗試使用定時鎖(
lock.tryLock(timeout)
),替代普通鎖機制