< type="text/網頁特效"> function windowTitle() { if (location.href.indexOf('is-external=true') == -1) { parent.document.title="Lock (Java Platform SE 6)"; } }
Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支援多個相關的 Condition 對象。
鎖是控制多個線程對共用資源進行訪問的工具。通常,鎖提供了對共用資源的獨佔訪問。一次只能有一個線程獲得鎖,對共用資源的所有訪問都需要首先獲得鎖。不過,某些鎖可能允許對共用資源並發訪問,如 ReadWriteLock 的讀取鎖。
synchronized 方法或語句的使用提供了對與每個對象相關的隱式監視器鎖的訪問,但卻強制所有鎖擷取和釋放均要出現在一個塊結構中:當擷取了多個鎖時,它們必須以相反的順序釋放,且必須在與所有鎖被擷取時相同的詞法範圍內釋放所有鎖。
雖然 synchronized 方法和語句的範圍機制使得使用監視器鎖編程方便了很多,而且還協助避免了很多涉及到鎖的常見編程錯誤,但有時也需要以更為靈活的方式使用鎖。例如,某些遍曆並發訪問的資料結果的演算法要求使用 "hand-over-hand" 或 "chain locking":擷取節點 A 的鎖,然後再擷取節點 B 的鎖,然後釋放 A 並擷取 C,然後釋放 B 並擷取 D,依此類推。Lock 介面的實現允許鎖在不同的作用範圍內擷取和釋放,並允許以任何順序擷取和釋放多個鎖,從而支援使用這種技術。
隨著靈活性的增加,也帶來了更多的責任。不使用塊結構鎖就失去了使用 synchronized 方法和語句時會出現的鎖自動釋放功能。在大多數情況下,應該使用以下語句:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
鎖定和取消鎖定出現在不同作用範圍中時,必須謹慎地確保保持鎖定時所執行的所有代碼用 try-finally 或 try-catch 加以保護,以確保在必要時釋放鎖。
Lock 實現提供了使用 synchronized 方法和語句所沒有的其他功能,包括提供了一個非塊結構的擷取鎖嘗試 (tryLock())、一個擷取可中斷鎖的嘗試 (lockInterruptibly()) 和一個擷取逾時失效鎖的嘗試 (tryLock(long, TimeUnit))。
Lock 類還可以提供與隱式監視器鎖完全不同的行為和語義,如保證排序、非重入用法或死結檢測。如果某個實現提供了這樣特殊的語義,則該實現必須對這些語義加以記錄。
注意,Lock 執行個體只是普通的對象,其本身可以在 synchronized 語句中作為目標使用。擷取 Lock 執行個體的監視器鎖與調用該執行個體的任何 lock() 方法沒有特別的關係。為了避免混淆,建議除了在其自身的實現中之外,決不要以這種方式使用 Lock 執行個體。
除非另有說明,否則為任何參數傳遞 null 值都將導致拋出 NullPointerException。
記憶體同步
所有 Lock 實現都必須 實施與內建監視器鎖提供的相同記憶體同步語義,如 The Java Language Specification, Third Edition (17.4 Memory Model) 中所描述的:
成功的 lock 操作與成功的 Lock 操作具有同樣的記憶體同步效應。
成功的 unlock 操作與成功的 Unlock 操作具有同樣的記憶體同步效應。
不成功的鎖定與取消鎖定操作以及重入鎖定/取消鎖定操作都不需要任何記憶體同步效果。
實現注意事項
三種形式的鎖擷取(可中斷、不可中斷和定時)在其效能特徵、排序保證或其他實現品質上可能會有所不同。而且,對於給定的 Lock 類,可能沒有中斷進行中的 鎖擷取的能力。因此,並不要求實現為所有三種形式的鎖擷取定義相同的保證或語義,也不要求其支援中斷進行中的鎖擷取。實現必需清楚地對每個鎖定方法所提供的語義和保證進行記錄。還必須遵守此介面中定義的中斷語義,以便為鎖擷取中斷提供支援:完全支援中斷,或僅在進入方法時支援中斷。
由於中斷通常意味著取消,而通常又很少進行中斷檢查,因此,相對於普通方法返回而言,實現可能更喜歡響應某個中斷。即使出現在另一個操作後的中斷可能會釋放線程鎖時也是如此。實現應記錄此行為
File: Professor.java
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
public class Professor {
@Id private int id;
private String name;
private long salary;
private int vacationDays;
@Version private int version;
@ManyToOne(cascade={CascadeType.REFRESH})
private Professor manager;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getSalary() {
return salary;
}
public void setSalary(long salary) {
this.salary = salary;
}
public int getVacationDays() {
return vacationDays;
}
public void setVacationDays(int vacation) {
this.vacationDays = vacation;
}
public Professor getManager() {
return manager;
}
public void setManager(Professor manager) {
this.manager = manager;
}
public String toString() {
return "Professor id: " + getId() + " name: " + getName() +
" vacation: " + getVacationDays() + " salary: " + getSalary();
}
}
File: ProfessorService.java
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.Query;
public class ProfessorService {
protected EntityManager em;
public ProfessorService(EntityManager em) {
this.em = em;
}
public void lockAllProfessors() {
Query query = em.createQuery("SELECT e FROM Professor e");
for(Object employee: query.getResultList()){
em.lock(employee, LockModeType.WRITE);
}
}
}
File: JPAUtil.java
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
public class JPAUtil {
Statement st;
public JPAUtil() throws Exception{
Class.forName("org.hsqldb.jdbcDriver");
System.out.println("Driver Loaded.");
String url = "jdbc:hsqldb:data/tutorial";
Connection conn = DriverManager.getConnection(url, "sa", "");
System.out.println("Got Connection.");
st = conn.createStatement();
}
public void executeSQLCommand(String sql) throws Exception {
st.executeUpdate(sql);
}
public void checkData(String sql) throws Exception {
ResultSet rs = st.executeQuery(sql);
ResultSetMetaData metadata = rs.getMetaData();
for (int i = 0; i < metadata.getColumnCount(); i++) {
System.out.print("t"+ metadata.getColumnLabel(i + 1));
}
System.out.println("n----------------------------------");
while (rs.next()) {
for (int i = 0; i < metadata.getColumnCount(); i++) {
Object value = rs.getObject(i + 1);
if (value == null) {
System.out.print("t ");
} else {
System.out.print("t"+value.toString().trim());
}
}
System.out.println("");
}
}
}
File: Main.java
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class Main {
public static void main(String[] a) throws Exception {
JPAUtil util = new JPAUtil();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ProfessorService");
EntityManager em = emf.createEntityManager();
ProfessorService service = new ProfessorService(em);
em.getTransaction().begin();
service.lockAllProfessors();
util.checkData("select * from Professor");
em.getTransaction().commit();
em.close();
emf.close();
}
}
File: persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence" version="1.0">
<persistence-unit name="JPAService" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.connection.url" value="jdbc:hsqldb:data/tutorial"/>
</properties>
</persistence-unit>
</persistence>