同步方法
仅当前执行线程能访问被synchronized的方法,若其他线程尝试访问此被synchronized修饰的方法,被挂起直到第一个线程该方法的调用完成(因为都竞争对象锁,谁先获得,谁先执行synchronized方法)。静态方法有不同的行为,只有一个执行线程能访问被synchronized修饰的静态方法,但是其他线程能访问该对象的非静态方法。
public synchronized void inc() {
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
synchronized (this) {//代码块
// Java code
}
在同步类中分配独立属性
public class ThreadTest {
private long shareData = 100;
private long shareData1 = 100;//shareData shareData1是独立的两个共享变量
private Object lock1 = new Object();//shareData的锁
private Object lock2 = new Object();//shareData1的锁
public void addData1() {
synchronized (lock1) {
shareData++;
}
}
public void addData2() {
synchronized (lock2) {
shareData1++;
}
}
}
在同步代码中使用条件
Java的Object对象实现了wait(),notify(),notifyAll()方法,一个线程可以在synchronized代码块内调用wait方法,若在之外调用,JVM会抛出IllegalMonitorStateException异常。当线程调用wait()方法时,JVM将线程进入sleep状态,释放object的锁对象,允许其他线程来执行synchronized代码块。在受保护代码块内使用notify()或notifyAll()来唤醒等待该object锁的线程。
public synchronized void set(){
while (storage.size()==maxSize){
try {
wait();//继续等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.offer(new Date());
System.out.printf("Set: %d",storage.size());
notifyAll();//唤醒其他等待此对象锁的线程
}
使用Lock同步代码块
相比synchronized代码块,Lock接口提供了额外的特性。
tryLock():尝试获取锁的控制,如果不能获得锁的控制,立即返回,不会将线程置入sleep状态,该方法返回boolean值,表明是否获得锁。
lock()方法:获取锁,若不能获得,被置入sleep状态,一直等待。
private final Lock queueLock=new ReentrantLock();
public void printJob(Object document){
queueLock.lock();
try {
Long duration=(long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName()+ ":
PrintQueue: Printing a Job during "+(duration/1000)+
" seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
queueLock.unlock();
}
}
使用lock时,注意避免死锁,典型的情况:thread A持有x,thread B持有y,此时线程A想要持有y,而线程B想要持有x,就出现死锁现象了。
使用读写锁同步数据访问
线程性能一个显著提升是ReadWriteLock接口的实现ReentrantReadWriteLock,它有两个锁,一个读锁,另一个是些锁。可以有多个线程同时使用读操作,但仅有一个线程使用写操作。当一个线程进行写操作,不能存在任何线程进行读操作。
private ReadWriteLock lock = new ReentrantReadWriteLock();
public double getPrice1() {
lock.readLock().lock();
double value=price1;
lock.readLock().unlock();
return value;
}
public double getPrice2() {
lock.readLock().lock();
double value=price2;
lock.readLock().unlock();
return value;
}
public void setPrices(double price1, double price2) {
lock.writeLock().lock();
this.price1=price1;
this.price2=price2;
lock.writeLock().unlock();
}
修改锁公平性
ReentrantLock和ReentrantReadWriteLock类提供一个boolean参数fair,允许用户控制它的行为,false是默认值,成为非公平模式(当有两个线程等待一个lock,lock必须选择其中一个来访问,选择时没有任何规则。为true时,称为公平模式(有多个线程等待这个锁时,lock选择等待时间最长的线程执行,lock()和unlock受此特性影响,单tryLock()傻等,所以不受此特性影响)。
Lock queueLock=new ReentrantLock(true);//公平锁
使用锁的多个条件
一个lock会与一个或多个条件关联,这些条件以Condition接口声明,提供了挂起线程和唤醒挂起线程的机制。
ReentrantLock lock = new ReentrantLock();
Condition lines = lock.newCondition();
Condition space = lock.newCondition();//lines和space配对使用
public void insert(String line) {
lock.lock();
try {
while (buffer.size() == maxSize) {//缓冲区已满
space.await();
}
buffer.offer(line);
System.out.printf("%s: Inserted Line: %d\n", Thread.
currentThread().getName(),buffer.size());
lines.signalAll();//已添加
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public String get() {
String line=null;
lock.lock();
try {
while ((buffer.size() == 0) &&(hasPendingLines())) {
lines.await();//缓冲区为空
}
if (hasPendingLines()) {
line = buffer.poll();
System.out.printf("%s: Line Readed: %d\n",Thread.
currentThread().getName(),buffer.size());
space.signalAll();//已取走
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return line;
}