多线程的Lock锁——ReentrantLock

在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增了Lock锁,同样可以实现同样的效果,并且扩展功能上也更加强大,比如嗅探锁定、多路分支通知等功能,而且使用上也更灵活。

        Lock锁分为两类:ReentrantLock和ReentrantReadWriteLock。

这里介绍ReentrantLock类的基础用法。

        ReentrantLock具有完全的排他性效果,即同一时刻只有一个线程拥有Lock对象锁。

        一个ReentrantLock对象可以创建多个Condition对象,可以根据不同的Condition,进行针对唤醒和等待Lock锁。

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
 
lock.lock();
.
.//需要同步的代码
.
lock.unlock();
 
condition.await();//进入等待状态,释放lock锁
condition.signal();//随机唤醒一个等待lock锁的线程
condition.signalAll();//唤醒所有condition条件下等待lock的线程

具体例子一(一对一生产与消费):

1、创建一个服务类,

package com.cjs.ConditionTestMantToMany;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue;//判断是否有值
 
    public void set() {
        try {
            lock.lock();
            while (hasValue == true) {
                condition.await();//进入等待,相当于wait()
            }
            System.out.println("set Print");
            hasValue = true;
            condition.signalAll();//唤醒所有正在等待的线程,相当于notifiAll()
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 
    public void get() {
        try {
            lock.lock();
            while (hasValue == false) {
                condition.await();//进入等待,相当于wait()
            }
            System.out.println("get Print");
            hasValue = false;
            condition.signalAll();//唤醒所有正在等待的线程,相当于notifiAll()
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
 
    }
}

2、创建两个线程类,调用服务类的方法,

package com.cjs.ConditionTest;
 
public class MyThreadA extends Thread {
    private MyService myService;
 
    public MyThreadA(MyService myService) {
        this.myService = myService;
    }
 
    @Override
    public void run() {
        for (int i=0; i<100;i++) {
            myService.set();
        }
    }
}
package com.cjs.ConditionTest;
 
public class MyThreadB extends Thread {
    private MyService myService;
 
    public MyThreadB(MyService myService) {
        this.myService = myService;
    }
 
    @Override
    public void run() {
        for (int i=0; i<100;i++) {
            myService.get();
        }
    }
}

3、创建Main类,用来启动程序,

package com.cjs.ConditionTest;
 
public class Run {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThreadA a = new MyThreadA(myService);
        a.start();
        MyThreadB b = new MyThreadB(myService);
        b.start();
    }
}

4、运行,查看控制台,get和set是交替打印,即一方面证明生产与消费属于一对一关系,另一方面说明ReentrantLock是互斥的。

具体例子二(多Condition条件的使用)

1、创建一个服务类

package com.cjs.MustUseMoreCondition_OK;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class MyService {
    private Lock lock = new ReentrantLock();
    public Condition conditionA = lock.newCondition();
    public Condition conditionB = lock.newCondition();
 
    public void awaitA() {
        try {
            lock.lock();
            System.out.println("begin awaitA时间为:"+ System.currentTimeMillis() + "ThreadName=" + Thread.currentThread().getName());
            conditionA.await();
            System.out.println("end awaitA时间为:"+System.currentTimeMillis()+"ThreadName=" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB() {
        try {
            lock.lock();
            System.out.println("begin awaitB时间为:"+ System.currentTimeMillis() + ", ThreadName=" + Thread.currentThread().getName());
            conditionB.await();
            System.out.println("end awaitB时间为:"+System.currentTimeMillis()+", ThreadName=" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
 
    public void signalAll_A() {
        try {
            lock.lock();
            System.out.println("signalAll_A时间为:" + System.currentTimeMillis() + ", ThreadName=" + Thread.currentThread().getName());
            conditionA.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public void signalAll_B() {
        try {
            lock.lock();
            System.out.println("signalAll_B时间为:" + System.currentTimeMillis() + ", ThreadName=" + Thread.currentThread().getName());
            conditionB.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

这里创建了两个Condition对象,分别是conditionA和conditionB。

2、创建两个线程类

package com.cjs.MustUseMoreCondition_OK;
 
public class ThreadA extends Thread {
    private MyService myService;
 
    public ThreadA(MyService myService) {
        this.myService = myService;
    }
 
    @Override
    public void run() {
        myService.awaitA();
    }
}
package com.cjs.MustUseMoreCondition_OK;
 
public class ThreadB extends Thread {
    private MyService myService;
 
    public ThreadB(MyService myService) {
        this.myService = myService;
    }
 
    @Override
    public void run() {
        myService.awaitB();
    }
}

3、创建一个Main类,

package com.cjs.MustUseMoreCondition_OK;
 
public class Run {
    public static void main(String[] args) {
        try {
            MyService myService = new MyService();
            ThreadA a = new ThreadA(myService);
            a.setName("A");
            a.start();
            ThreadB b = new ThreadB(myService);
            b.setName("B");
            b.start();
            Thread.sleep(3000);
            myService.signalAll_A();
            //myService.signalAll_B();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上面注释了myService.signalAll_B();表示不换醒conditionB条件的线程。

4、运行程序

 

 运行结果中,只有conditionA的线程被唤醒,同时执行结束;但是程序并没有结束,还有红色的方框显示出来,说明还有其他线程正在等待,这些等待的线程就是conditionB条件的线程。这时去掉myService.signalAll_B();前面的注释,再运行一次程序。

        另外补充,ReentranrLock类还有两种锁,分别是:公平锁和非公平锁

1、公平锁:Lock  lock = new ReentrantLock(true);

表示获取锁的顺序是几乎按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。

2、非公平锁:Lock  lock = new ReentrantLock(false);

表示一种获取锁的抢占机制,就是随机获取锁。

 

原文地址:https://www.cnblogs.com/SysoCjs/p/10319484.html