myLock.lock(); //a ReentrantLock object
try
{
critical section
}
finally
{
myLock.unlock(); //make sure the lock is unlocked even if an exception is thrown
}
Class Bank
{
private Condition sufficientFunds;
...
public Bank()
{
...
sufficientFunds = bankLock.newCondition();
}
}
在并发代码块中使用条件对象的实例:public void transfer(int from,int to,int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
sufficientFunds.await();
//transfer funds
...
sufficientFunds.singalAll();
}
finally
{
bankLock.unlock();
}
}
总结:锁和条件的关键之处:
- 锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码。
- 锁可以管理试图进入被保护代码段的线程。
- 锁可以拥有一个或多个相关的条件对象。
- 每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程。
synchronize关键字:从1.0版开始,java中的每一个对象都有一个内部锁。如果一个方法用synchronize关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。
换句话说,
public synchronized void method()
{
method body
}
等价于
public void method()
{
this.intrinsicLock.lock();
try
{
method body
}
finally
{
this.intrinsicLock.unlock();
}
}
内部对象锁只有一个相关条件。wait方法添加一个线程到等待集中,notifyAll/notify方法解除等待线程的阻塞状态。
注意:wait、notifyAll以及notify方法时Object类的final方法。Condition方法必须被命名为await、signalAll和signal以便它们不会与那些方法发生冲突。
内部锁和条件的局限:
1、不能试图中断获得锁的线程。
2、获得锁时不能设定超时。
3、每个所只有单一的条件可能是不够的。
在代码中应该使用哪种方法?Lock和Condition对象还是同步方法?
1、最好既不用Lock/Condition也不使用synchronized关键字。建议使用java.util.concurrent包中的一种机制,它会为你处理所有的加锁。
2、如果synchronized关键字适合你的程序。那么尽量使用它。这样可以减少编写代码的数量,减少出错的几率。
3、如果特别需要Lock/Condition结果提供的独有特性时,才使用Lock/Condition。
监视器(monitor)的特性:
- 监视器是只包含私有域的类。
- 每个监视器类的对象有一个相关的锁。
- 使用该锁对所有的方法进行加锁。
- 该锁可以有任意多个相关条件。
java对象在哪几方面不同于监视器,使得线程的安全性下降?
- 域不要求必须是private。
- 方法不要求必须是synchronized。
- 内部锁对客户是可用的。
在同步时,使用现代的处理器和编译器,出错的可能性很大的原因:
- 多处理器的计算机能够暂时在寄存器或本地内存缓冲区中保存内存中的值。结果是,运行在不同处理器上的线程可能在同一内存位置取到不同的值。
- 编译器可以改变指令执行的顺序以使吞吐量最大化。
volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域可能被另一个线程并发更新的。(个人理解:对一个变量声明为volatile后其他线程可以看到读取,但是不能编辑,并且其他线程看到的都是最新更新的值。)
除了使用锁或volatile修饰符,还有另外一种情况可以安全地访问一个共享域,即这个域声明为final时。其他线程会在构造函数完成构造之后才看到用final修饰的变量。
eg:final Map<String,Double> accounts = new HashMap<>();
读/写锁:
java.util.concurrent.locks包定义了两个锁类:RenntrantLock类和ReentrantReadWriteLock类。