如果想要static方法和普通的方法在同步情况下不能同时执行,单靠synchronized实现非常有难度,因为静态方法以class对象为锁,普通方法以具体的具体的对象为锁,java提供的另一种同步机制利用Lock接口及其实现类,比synchronized更加灵活。
synchronized可以支持更灵活的同步代码块结构,synchronized只能在synchronized块结构中获取和释放锁,Lock的方式更加灵活,可以不出现在同一个块结构中。
Lock方式提供了更丰富的功能,lock方式允许读写分离,性能更好,具有tryLock方法。
java并发包下三个重要的类Condition、ReentrantLock、ReentrantReadWriteLock是Lock的主要实现方式。
实现了Lock接口:
利用lock方式实现同步:
package com.sync.demo;
import java.io.Serializable;
import java.util.concurrent.locks.ReentrantLock;
public class Demo9 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
long number = 100;
NumberData numberData = new NumberData(number);
Thread thread1 = new Thread(new User(numberData, 10));
Thread thread2 = new Thread(new User(numberData, -50));
Thread thread3 = new Thread(new User(numberData, 10));
Thread thread4 = new Thread(new User(numberData, -10));
Thread thread5 = new Thread(new User(numberData, -40));
Thread thread6 = new Thread(new User(numberData, 10));
Thread thread7 = new Thread(new User(numberData, -10));
Thread thread8 = new Thread() {
@Override
public void run() {
NumberData.staticFun1();
}
};
Thread thread9 = new Thread() {
@Override
public void run() {
NumberData.staticFun2();
}
};
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread7.start();
thread8.start();
thread9.start();
}
static class NumberData implements Serializable{
private long number;
public NumberData(long number) {
this.number = number;
}
public long getNumber() {
return number;
}
public void setNumber(long number) {
this.number = number;
}
public void add(long num) {
lock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
number = number + num;
System.out.println("=======add============ "+num+" "+number);
lock.unlock();
}
public void del(long num) {
lock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
number = number + num;
System.out.println("=======del============ "+num+" "+number);
lock.unlock();
}
public static void staticFun1() {
lock.lock();
for(int i=0;i<4;i++) {
try {
Thread.sleep(500);
System.out.println("========staticFun1========");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
public static void staticFun2() {
lock.lock();
for(int i=0;i<4;i++) {
try {
Thread.sleep(500);
System.out.println("========staticFun2========");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
static class User implements Runnable{
private NumberData numberData;
private long number;
public long getNumber() {
return number;
}
public void setNumber(long number) {
this.number = number;
}
public NumberData getNumberData() {
return numberData;
}
public void setNumberData(NumberData numberData) {
this.numberData = numberData;
}
public User(NumberData numberData,long num) {
super();
this.numberData = numberData;
this.number = num;
}
@Override
public void run() {
if (number > 0) {
numberData.add(number);
}else {
numberData.del(number);
}
}
}
}
result:
=======del============ -50 50
=======add============ 10 60
=======add============ 10 70
=======del============ -10 60
=======del============ -40 20
=======add============ 10 30
=======del============ -10 20
========staticFun1========
========staticFun1========
========staticFun1========
========staticFun1========
========staticFun2========
========staticFun2========
========staticFun2========
========staticFun2========
2 Condition
在lock中利用Condition,一个锁可以关联一个或多个条件,使运行线程获取锁并且查看等待某一个条件是否满足,如果不满足则挂起直到某个线程唤醒它们。condition提供的挂起线程和唤起线程的机制,和object的wait,notify功能类似,Condition用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。Condition由Lock的newCondition()方法生成,condition的使用必须在lock和unLock之间,如果条件未满足需要在while循环中判断。lock 必须在 finally 块中释放。await()释放锁,然后挂起线程,其实是把线程放入了Condition引用的等待队列中。
signal作用类似notify,但大家都知道同时只有一个线程能够获取锁,其他线程仍要等待。其实signal()方法只是将Condition等待队列头结点移出队列,此时该线程节点还是阻塞的,同时将该节点的线程重新包装加入AQS等待队列(调用Lock.lock时会被加入AQS队列,调用await时会从AQS中移除,加入到Condition的等待队列),当调用unlock方法时会唤醒AQS等待队列中的节点,如果唤醒的节点不是刚刚移出的节点,那么刚刚移出Condition等待队列的节点还将继续被阻塞,否则节点会被唤醒。
Condition自己维护了一个等待信号的队列,并在适时的时候将结点加入到AQS的等待队列中来实现的唤醒操作。
具体Condition的原理会以后分析。
Condition可以为多个线程间建立不同的Condition, 利用使用lock/condition,可以实现多个阻塞队列,signalAll只会唤起某个阻塞队列下的阻塞线程。
Condition实现生产者消费者模式
package com.sync.demo;
import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo11 {
private int queueSize = 10 ;//最大仓库容量
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
private Lock lock = new ReentrantLock();
private Condition fullCondition = lock.newCondition();
private Condition emptyCondition = lock.newCondition();
class Consumer implements Runnable{
@Override
public void run() {
consume();
}
//condition 必须在lock的保护之下
private void consume() {
while(true){
lock.lock();
try {
//循环中判断
while(queue.size() == 0){
try {
System.out.println("===========产品为空无法消费================");
emptyCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
//通知可以生产了(如果被阻塞挂起的话)
fullCondition.signal();
System.out.println("==============消费一个剩余==========="+queue.size());
} finally{
lock.unlock();
}
}
}
}
class Producer implements Runnable{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
lock.lock();
try {
while(queue.size()== queueSize){
try {
System.out.println("================商品已经满仓了,不能生产=============");
fullCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产一个
queue.offer(1);
System.out.println("==============生产一个剩余==========="+queue.size());
//通知不为空了
emptyCondition.signal();
} finally{
lock.unlock();
}
}
}
}
public static void main(String[] args) {
Demo11 demo = new Demo11();
Consumer cus = demo.new Consumer();
Producer pro = demo.new Producer();
Producer pro2 = demo.new Producer();
Thread thread1 = new Thread(cus);
Thread thread2 = new Thread(pro);
thread1.start();
thread2.start();
}
}