(Iv) Lock, ReentrantLock, use ReentrantReadWriteLock classes and related api --- synchronized Advanced

This blog records the Lock, ReentrantLock, use ReentrantReadWriteLock class as well as some of its api:

In addition codeword is not easy ~ ~ "java multithreaded programming core technology," read the book so cool

Preface: Before order to solve the problem of non-thread-safe multi-threaded, using the synchronized . The next record is an upgraded version of his ReentrantLock , more flexible, more controllable, and ReentrantReadWriteLock class is a supplement to ReentrantLock class, can submit between efficiency under certain conditions

The following are among the first look at what api, as well as synchronized and how it corresponds.

 

Previously used lock synchronization is complete synchronization code written in the synchronized block of now we use  

Lock lock = new ReentrantLock();

To declare a lock, he has two methods

Lock.lock (); and lock.unlock (); which is supporting two code sync block is between them.

As before, lock () method makes the current thread holds listener objects, like the specific rules and synchronized too,

For example the following example, the MyService piece of code lock, it calls the custom thread class

MyService.java

package 第四章;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
    private Lock lock = new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for(int i=0;i<5;i++){
            System.out.println(i+"线程:"+Thread.currentThread().getName());
        }
        lock.unlock();
    }
}
View Code

MyThread.java

package 第四章;

public class MyThread extends Thread {
    private MyService myService;

    public MyThread(MyService myService) {
        super();
        this.myService = myService;
    }

    public void run(){
        myService.testMethod();
    }
}
View Code

test.java

package 第四章;

public class test {
    public static void main(String[] args){
        MyThread[] threads = new MyThread[5];
        MyService myService = new MyService();
        for(int i=0;i<5;i++){
            threads[i] = new MyThread(myService);
            threads[i].start();
        }
    }
}
View Code

运行结果:

 

 可以看到线程之间是同步执行的,当然前提是同一个MyService对象。

之前的wait/notify,用Condition对象来替换:

效率提高的地方以及原因:

Condition对象可以对同一个锁声明多个,相当于每当让线程等待时,他都有自己的唤醒condition,换句话说,每一个线程都可以注册一个Condition,这样当我们唤醒线程的时候,就可以唤醒指定的线程,比如之前的生产者消费者模型之中的假死现象,我们使用过notifyAll()来解决的,但是这种方法唤醒了所有的线程,让所有线程都去争抢cpu,但是我们事实上指向唤醒异类线程,并不想唤醒同类,全部唤醒的话,效率是一个问题。那么现在,给每一个线程都注册

一个Condition,这样子唤醒时候,我们就可以唤醒指定的线程,提高了效率,也更加灵活。

下面的是一个简单的await/signal例子,展示了基本的使用:await类似之前的wait,signal类似于notify:signalAll()唤醒全部

更改之前的MyService.java

condition.await()让线程阻塞,condition.signal()随机唤醒一个由当前condition注册的线程

 

package 第四章;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class MyService {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void testMethod(){
        try{
        lock.lock();
        System.out.println("即将开始循环");
        condition.await();
        for(int i=0;i<2;i++){
            System.out.println(i+"线程:"+Thread.currentThread().getName());
        }
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signal(){
        try{
            lock.lock();
            this.condition.signal();
            System.out.println("唤醒了一个线程");
        }finally {
            lock.unlock();
        }
    }
}
View Code

 

MyThread.java不变

test.java:先让线程全部阻塞,然后调用自定义的signal方法唤醒线程,

package 第四章;

public class test {
    public static void main(String[] args){
        MyThread[] threads = new MyThread[5];
        MyService myService = new MyService();
        for(int i=0;i<5;i++){
            threads[i] = new MyThread(myService);
            threads[i].start();
        }
        try{
            Thread.sleep(1000);
            myService.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
View Code

运行结果如下:

可以看到,我们成功唤醒了一个线程。

下面的例子唤醒了一个指定的线程

MyService.java:根据当前线程的名字让指定的Condition对象等待,并书写两个唤醒不同的Condition对象注册的线程

package 第四章;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class MyService {
    private Lock lock = new ReentrantLock();
    public Condition conditionA = lock.newCondition();
    public Condition conditionB = lock.newCondition();
    public void testMethod(){
        try{
            lock.lock();
            System.out.println("线程"+Thread.currentThread().getName()+"等待中...");
            if(Thread.currentThread().getName().equals("A"))
                conditionA.await();
            else
                conditionB.await();
            for(int i=0;i<2;i++){
                System.out.println(i+"线程:"+Thread.currentThread().getName());
        }
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signalA(){
        try{
            lock.lock();
            this.conditionA.signal();
            System.out.println("唤醒了A线程");
        }finally {
            lock.unlock();
        }
    }
    public void signalB(){
        try{
            lock.lock();
            this.conditionB.signal();
            System.out.println("唤醒了B线程");
        }finally {
            lock.unlock();
        }
    }
}
View Code

test.java,启动A,B两个线程,只唤醒A线程

package 第四章;

public class test {
    public static void main(String[] args){
        MyService myService = new MyService();
        MyThread myThreadA = new MyThread(myService);
        myThreadA.setName("A");
        MyThread myThreadB = new MyThread(myService);
        myThreadB.setName("B");
        myThreadA.start();
        myThreadB.start();
        try{
            Thread.sleep(1000);
            myService.signalA();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
View Code

运行结果:

根据代码,我们可以看到可以通过不同Condition对象来唤醒指定的线程。

用处:

1.可以想到,如果用Lock来解决之前的多消费多生产者时的假死问题,我们可以将生产者统一注册一个Condition,消费者统一注册一个Condition,每一次唤醒对方的Condition,这样子就不会出现连续唤醒同类导致假死的情况了,并且可以避免唤醒所有线程,导致效率低下。

2.我们也可以按照我们想要的顺序进行唤醒,只要你注册了正确的Condition对象

 

公平锁和非公平锁:

比较好理解,公平锁相当于一个队列,先进先出,先运行的线程先拿到锁,后运行的后拿到锁,按照顺序来,非公平锁就是锁的抢占是随机的,没有顺序。

默认是非公平锁,创建Lock时加上true参数即为公平锁:

Lock lock =new ReentrantLock(true);

 

下面介绍一些ReentrantLock的api,

一般在一些定制化的情况可能会用到,emmm,这块先了解一下,知道有这些就行,emmm,说实话目前我感觉这个没啥用,有个印象,不过注意使用这些API使,必须以下面这种方式new对象

ReentrantLock lock = new ReentrantLock();

(lock.)GetHoldCount():查询当前线程有保持着几个lock锁,简单来讲就是当前线程调用了几次lock()方法

GetQueueLength():有多少个线程在等待获取当前锁,可以理解为有多少个没有拿到当前锁,

getWaitQueueLength(Condition condition):有多少个线程处于阻塞状态,并且是执行了参数Condition对象所对应的await()方法导致阻塞的。

hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取当前锁

hasQueuedThreads():查询是否有线程正在等待获取当前锁

hasWaiters(Condition):查询是否有线程是由于调用了参数Condition.await()导致阻塞的。

isHeldByCurrentThread():查询当前线程是否持有当前锁

isLocked():当前锁是否被某个线程持有

awaitUninterruptibly():这也是一种让当前线程阻塞的方法,不过await调用之后如果再使用Interrupt等代码阻塞当前进程会报异常,但是这个不会,相当于让当前线程变成可以阻塞的线程,,,,不懂有撒用

awaitUntil(Date):阻塞当前线程,如果在指定时间之前还没有被唤醒,则唤醒他。参数也可以传Calendar.getTime(),Calendar类用于处理时间

 

ReentrantReadWriteLock类

之前的ReentrantLock相当于同一时间只有一个线程在执行代码。但是在不涉及更改实例变量的代码之中,我们可以允许异步运行来加快效率, 而一些涉及到更改实例变量的代码,这时候同步执行(这时候异步可能出现非线程安全),这样可以在一定程度上加快效率,这就是这个类的作用。

简单来说,我们一般有读写两个操作,如果多个线程执行读操作,ok,异步执行,如果多个线程有的执行读,有的写,ok,同步执行,这个类就是自动完成这个事情,你只需要在锁时使用不同类型的锁就行。

下面是一个例子,读读异步(其他全部同步):
ReadAndWrite.java  代表具体的操作,读,写,输出当前操作以及时间,sleep()模拟操作耗费的时间

package 第四章;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadAndWrite {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read(){
        try{
            lock.readLock().lock();
            System.out.println("读操作"+System.currentTimeMillis());
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }
    public void write(){
        try{
            lock.writeLock().lock();
            System.out.println("写操作"+System.currentTimeMillis());
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
}
View Code

MyThread2.java:里面有两个java类,一个执行读操作,一个写操作

package 第四章;

class MyThreadRead extends Thread{
    private ReadAndWrite readAndWrite;

    public MyThreadRead(ReadAndWrite readAndWrite) {
        this.readAndWrite = readAndWrite;
    }

    public void run(){
        this.readAndWrite.read();
    }
}
class MyThreadWrite extends Thread{
    private ReadAndWrite readAndWrite;

    public MyThreadWrite(ReadAndWrite readAndWrite) {
        this.readAndWrite = readAndWrite;
    }

    public void run(){
        this.readAndWrite.write();
    }
}
View Code

test.java:  创建三个读线程

package 第四章;

public class test {
    public static void main(String[] args){
           ReadAndWrite readAndWrite = new ReadAndWrite();
           MyThreadRead reads[] = new MyThreadRead[3];
           for(int i=0;i<3;i++) {
               reads[i] = new MyThreadRead(readAndWrite);
               reads[i].start();
           }
        }
    }
View Code

运行结果:

可以看到,三个读操作时同时执行的。

下面更改test.java,创建三个读线程,三个写线程:

test.java

package 第四章;

public class test {
    public static void main(String[] args){

           ReadAndWrite readAndWrite = new ReadAndWrite();
        MyThreadWrite writes[] = new MyThreadWrite[3];
        for(int i=0;i<3;i++) {
            writes[i] = new MyThreadWrite(readAndWrite);
            writes[i].start();
        }
           MyThreadRead reads[] = new MyThreadRead[3];
           for(int i=0;i<3;i++) {
               reads[i] = new MyThreadRead(readAndWrite);
               reads[i].start();
           }

        }
    }
View Code

运行:

 

可以看到,写操作之间是互斥的,相当于同步,一个一个执行的,读的时候就是异步的,

 ,,好嘞,就演示这几个,其他的都同理,只有读读是异步的,读写同步,你可以交替着start看一下,如下:

 

 好滴,第四章就这些暂时。。

 

Guess you like

Origin www.cnblogs.com/eenio/p/11390950.html
Recommended