一、线程锁(Lock)
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。 它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition 。
示例代码:
public class LockTest {
public static void main(String[] args) {
new LockTest().init();
}
private void init() {
final Outputer outputer = new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("aaaaaaaaaaa");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("bbbbbbbbbb");
}
}
}).start();
}
static class Outputer {
Lock lock = new ReentrantLock();
public void output(String name) {
int len = name.length();
lock.lock();
try{
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}finally {
//确保锁的释放,当获取多个锁时,它们必须以相反的顺序被释放
lock.unlock();
}
}
}
}
锁是用于通过多个线程控制对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读锁。
二、读写锁ReadWriteLock
读锁允许并发访问,写锁则要求独占访问。
读写锁测试:
/**
* 读锁与读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由JVM控制的
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3();
for(int i = 0;i<3;i++ ){
new Thread(){
public void run(){
while(true){
q3.get();
}
}
}.start();
//Lambda表达式
new Thread( () -> {
while(true){
q3.put(new Random().nextInt(10000));
}
}).start();
}
}
}
class Queue3{
//共享数据,一次只能有一个线程写该数据,但是可以多个线程同时读
private Object data = null;
ReadWriteLock rwl = new ReentrantReadWriteLock();
//读数据
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" be ready to read data ");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName()+" have read data "+ data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
//写数据
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" be ready to write data ");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName()+" have write data "+ data);
}catch (Exception e){
e.printStackTrace();
}finally {
rwl.writeLock().unlock();
}
}
}
实际应用,可用来实现缓存功能(以下为伪代码)
public class CacheDemo {
private Map<String,Object> cache = new HashMap<String,Object>();
public static void main(String[] args) {
}
ReadWriteLock lock = new ReentrantReadWriteLock();
//缓存系统
public Object getData(String key){
lock.readLock().lock();
Object obj = null;
try{
//从缓冲中取数据
obj = cache.get(key);
//如果没有数据,则释放读锁,锁写锁,从数据库中读数据写入对象
if(obj == null){
lock.readLock().unlock();
lock.writeLock().lock();
try {
//为防止多次查数据库,降低性能,所以多做一次判断
if(obj==null){
obj ="queryDB";
}
} finally {
//确保写锁释放,在finally中释放写锁
lock.writeLock().unlock();
}
lock.readLock().lock();
}
}finally {
lock.readLock().unlock();
}
return obj;
}
}
三、条件阻塞Condition
如同this.wait()和this.notify需要和synchronized配合使用,而Condition需要和lock配合使用,condition代替传统的wait和notify实现线程之间的通信。
示例代码:
1、需求:主线程执行100次,子线程执行10次,交替执行,共循环50次
public class ConditionCommunication {
public static void main(String[] args) {
final Business business = new Business();
//子线程执行方法
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<50;i++){
business.sub(i);
}
}
}).start();
//主线程执行方法
for(int i = 0;i<50;i++){
business.main(i);
}
}
}
class Business{
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
boolean isSub = true;
//子线程调用的方法
public void sub(int i){
lock.lock();
try{
//用while是防止虚假唤醒
while(!isSub){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int j=0;j<10;j++){
System.out.println("sub method "+j+" loop of "+i);
}
isSub = false;
condition.signal();
}finally {
lock.unlock();
}
}
//主线程调用的方法
public void main(int i){
lock.lock();
try{
while(isSub){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int j=0;j<100;j++){
System.out.println("main method "+j+" loop of "+i);
}
isSub = true;
condition.signal();
}finally {
lock.unlock();
}
}
}
2、阻塞队列,缓冲区的实现
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;
//存入一个数据后,计数器+1
++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;
--count;
//如果存数据阻塞,则通知存数据
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
3、Condition 可用于多个线程之间的通信操作,例如1–>2–>3–>1反复执行
public class ThreeThreadCommunication {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<50;i++){
business.sub2(i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i<50;i++){
business.sub3(i);
}
}
}).start();
for(int i = 0;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();
int shouldSub = 1;
public void sub3(int i) {
lock.lock();
try {
//用while是防止虚假唤醒
while (shouldSub != 3) {
try {
condition3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 0; j < 10; j++) {
System.out.println("sub3 method " + j + " loop of " + i);
}
shouldSub = 1;
condition1.signal();
} finally {
lock.unlock();
}
}
public void sub2(int i) {
lock.lock();
try {
while (shouldSub!=2) {
try {
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 0; j < 10; j++) {
System.out.println("sub2 method " + j + " loop of " + i);
}
shouldSub = 3;
condition3.signal();
} finally {
lock.unlock();
}
}
public void main(int i) {
lock.lock();
try {
while (shouldSub!=1) {
try {
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 0; j < 100; j++) {
System.out.println("main method " + j + " loop of " + i);
}
shouldSub = 2;
condition2.signal();
} finally {
lock.unlock();
}
}
}
}