Local锁:
Local锁也是在java并发库java.util.concurrent中,不过Lock是一个接口,需要使用时必须new出Lock的具体实现类。
那么,Local锁是怎样使用呢,还是以账户存取款为例子,通过和synchronized锁的对比一看就会明白:
public class Test {
public static int cash = 100;
public static void main(String[] args) {
Thread thread2 = new Thread(new Runnable() {
public void run() {
//synchronized(this)//此时锁不起作用
synchronized(Test.class){
System.out.println("run查看账户,余额为" + cash);
cash += 1000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cash2 = " + cash);
}
}
}
);
thread2.start();
Thread thread1 = new Thread(new Runnable() {
public void run() {
//synchronized(this)此时锁不起作用,必须用Test1.class才起作用
synchronized(Test.class){
System.out.println("m查看账户,余额为" + cash);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cash = cash - 100;
System.out.println("cash1 = " + cash);
}
}
});
thread1.start();
}
}
在学习synchronized关键字的时我们讨论了这个小例子,当synchronized()中的参数为this时,是不起作用的。因为两个
Thread中this指的不是同一个东西。只有使用Test..class的时候,锁才起作用。
我们将synchronized关键字换为Lock锁:
因为Lock是一个接口,所以我们使用时就要new出Lock锁的具体实现:
Lock lock = new ReentrantLock();
lock.lock();//上锁
lock.unlock();//解锁
修改后:
public class Test {
public static int cash = 100;
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
Thread thread2 = new Thread(new Runnable() {
public void run() {
try{
lock.lock();//加锁
{
System.out.println("run查看账户,余额为" + cash);
cash += 1000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cash2 = " + cash);
}
}catch (Exception e) {
e.printStackTrace();
}finally{
//关闭锁
lock.unlock();
}
}
}
);
thread2.start();
Thread thread1 = new Thread(new Runnable() {
public void run() {
try{
lock.lock();//加锁
{
System.out.println("m查看账户,余额为" + cash);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cash = cash - 100;
System.out.println("cash1 = " + cash);
}
}catch(Exception e){
e.printStackTrace();
}
lock.unlock();//关闭锁
}
});
thread1.start();
}
}
此时的输出结果:
run查看账户,余额为100
cash2 = 1100
m查看账户,余额为1100
cash1 = 1000
结论:
程序运行正常。通过这个例子我们就能看出,Lock锁通过new出来,加了这把锁的所有方法不论在哪,都实现了线程阻塞。
相当于synchronized(常量),是个万能锁。
一个线程要加不同的锁,那么多new几个Lock,相同的lock加在需要阻塞的方法上就行了。
和synchronized不同的是,在线程执行完以后,要关闭锁unlock(),如果不关闭,其他在等待的线程就永远被锁在外面了。
比如new出lock和lock1,lock加在了方法A()和B()上,lock1加在了方法C()和D()上,那么,一旦有线程进入方法A(),就不会有线程再进入方法B(),同理,一旦有线程进入方法C(),就不会有线程进入方法D();但是A()和C(),B()和D()之间并没有形成阻塞。
Lock锁更加面向对象,因为lock本身就是一个对象。
Condition
Condition,Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
例子1:
启动两个线程,子线程运行10次,主线程运行10次,然后又子线程运行10次,主线程运行10次,如此反复50次:
public class Test{
public static void main(String[] args) {
final Busniess test = new Test().new Busniess();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i < 51; i++) {
test.sub(i);
}
}
}).start();
for (int i = 1; i < 51; i++) {
test.main(i);
}
}
class Busniess {
boolean isSub = true;
Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void sub(int i) {
lock.lock();
try {
while (!isSub) {
//this.wait();线程等待,保证子线程先运行
condition.await();
}
for (int j = 1; j < 11; j++) {
System.out.println("sub运行第" + i + "轮,第" + j + "次");
}
isSub = false;
//this.notifyAll();线程唤醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public synchronized void main(int i) {
try {
lock.lock();
while (isSub) {
//this.wait();
condition.await();
for (int j = 1; j < 11; j++) {
System.out.println("main运行第" + i + "轮,第" + j + "次");
}
isSub = true;
//this.notifyAll();
condition.signalAll();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
打印结果:
sub运行第1轮,第1次
sub运行第1轮,第2次
sub运行第1轮,第3次
sub运行第1轮,第4次
sub运行第1轮,第5次
sub运行第1轮,第6次
sub运行第1轮,第7次
sub运行第1轮,第8次
sub运行第1轮,第9次
sub运行第1轮,第10次
main运行第1轮,第1次
main运行第1轮,第2次
main运行第1轮,第3次
main运行第1轮,第4次
main运行第1轮,第5次
main运行第1轮,第6次
main运行第1轮,第7次
main运行第1轮,第8次
main运行第1轮,第9次
main运行第1轮,第10次
......
在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现。
Condition
实例实质上被绑定到一个锁上。要为特定Lock
实例获得Condition
实例,请使用其newCondition()
方法。
Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition,具体看下面的例子:
} 假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存put 线程和take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个Condition
实例来做到这一点。
class BoundedBuffer {
final Lock lock = new ReentrantLock();//锁对象
final Condition notFull = lock.newCondition();//写线程条件
final Condition notEmpty = lock.newCondition();//读线程条件
final Object[] items = new Object[100];//缓存队列
int putptr/*写索引*/, takeptr/*读索引*/, count/*队列中存在的数据个数*/;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)//如果队列满了
notFull.await();//阻塞写线程
items[putptr] = x;//赋值
if (++putptr == items.length) putptr = 0;//如果写索引写到队列的最后一个位置了,那么置为0
++count;//个数++
notEmpty.signal();//唤醒读线程
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)//如果队列为空
notEmpty.await();//阻塞读线程
Object x = items[takeptr];//取值
if (++takeptr == items.length) takeptr = 0;//如果读索引读到队列的最后一个位置了,那么置为0
--count;//个数--
notFull.signal();//唤醒写线程
return x;
} finally {
lock.unlock();
}
}
}
例子2:
以此类推,如果有三个线程,想让线程1运行完以后运行线程2,线程2运行完以后运行线程3,线程3运行完以后又运行线程1,那么该如何来实现呢?
public class ThreeConditionCommunication {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub2(i);
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub3(i);
}
}
}).start();
for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
static class Business {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int shouldSub = 1;
public void sub2(int i) {
lock.lock();
try {
while (shouldSub != 2) {
try {
condition2.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub2 thread sequence of " + j
+ ",loop of " + i);
}
shouldSub = 3;
condition3.signal();
} finally {
lock.unlock();
}
}
public void sub3(int i) {
lock.lock();
try {
while (shouldSub != 3) {
try {
condition3.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 20; j++) {
System.out.println("sub3 thread sequence of " + j
+ ",loop of " + i);
}
shouldSub = 1;
condition1.signal();
} finally {
lock.unlock();
}
}
public void main(int i) {
lock.lock();
try {
while (shouldSub != 1) {
try {
condition1.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("main thread sequence of " + j
+ ",loop of " + i);
}
shouldSub = 2;
condition2.signal();
} finally {
lock.unlock();
}
}
}
}
也就是说:Condition可以控制多个线程之间的运行顺序。