多线程4-Lock的使用

之前学习了通过synchronized关键字实现线程同步效果。除此之外,通过Lock对象来实现同步效果。


知识点

1- ReentrantLock类的使用
2- ReentrantReadWriteLock类的使用


ReentrantLock类

案例说明ReentrantLock可实现线程同步效果。

运行类:

package ReentrantLock_part1;

public class Test {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        Thread1 thread1 = new Thread1(service1);
        Thread1 thread2 = new Thread1(service1);
        Thread1 thread3 = new Thread1(service1);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

线程类:

package ReentrantLock_part1;

public class Thread1 extends Thread{
    private Service1 service1;
    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.task();
    }   
}

业务类:

package ReentrantLock_part1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Service1 {
    private Lock lock = new ReentrantLock();
    public void task(){
        //lock()方法获得锁
        lock.lock();            
        for(int i=0;i<5;i++){
            System.out.println("当前线程为: "+Thread.currentThread().getName());
        }
        //unlock()方法释放锁
        lock.unlock();
    }
}

运行结果:

当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-0
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-1
当前线程为: Thread-2
当前线程为: Thread-2
当前线程为: Thread-2
当前线程为: Thread-2
当前线程为: Thread-2

结论为:该ReentrantLock类的lock()和unlock()方法确实可以实现线程的同步效果。


实现等待/通知效果

synchronized使用wait / nitify实现。而在ReentrantLock类中,使用Condition对象实现。
不同点:wait / nitify的唤醒是随机唤醒的。Condition对象可实现选择性唤醒
测试工程代码如下:
运行类:

package ReentrantLock_part1;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Service1 service1 = new Service1();
        Thread1 thread1 = new Thread1(service1);
        thread1.start();
        Thread.sleep(50);
        Thread2 thread2 = new Thread2(service1);
        thread2.start();
    }
}

await线程类:

package ReentrantLock_part1;

public class Thread1 extends Thread{
    private Service1 service1;

    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.await();
    }

}

signla线程类:

package ReentrantLock_part1;

public class Thread2 extends Thread{
    private Service1 service1;

    public Thread2(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.signal();
    }

}

业务类:

package ReentrantLock_part1;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Service1 {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void await(){
        try {
            //lock()方法获得锁
            lock.lock();            
            System.out.println("await时间为: "+System.currentTimeMillis()/1000);
            Thread.sleep(2000);
            condition.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //unlock()方法释放锁
        lock.unlock();
    }
    public void signal(){
        lock.lock();
        System.out.println("signal时间为 : "+System.currentTimeMillis()/1000);
        condition.signal();
        lock.unlock();
    }
}

运行结果如下:

await时间为: 1526543282
signal时间为 : 1526543284

结论:可以实现线程的等待/唤醒功能。
扩展:之前说到ReentrantLock类的Condition对象可以选择性唤醒线程,实现方式是在业务类中创建多各Condition对象,不同线程使用同样的ReentrantLock对象所以可以实现线程同步效果,而不同的Condition对象意味着各个线程拥有不同的“对象监视器”,所以可以通过指定对象监视器的signal()方法或者signalAll()方法唤醒受到该对象监视的所有线程,而拥有其他对象监视器的线程不被唤醒。由此实现了线程的选择性唤醒。


验证一对一生产者消费者模式

工程代码如下:
运行类:

package ReentrantLock_part1;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Service1 service1 = new Service1();
        Thread1 thread1 = new Thread1(service1);
        thread1.start();
        Thread2 thread2 = new Thread2(service1);
        thread2.start();
    }
}
生产者线程:
package ReentrantLock_part1;

public class Thread1 extends Thread{
    private Service1 service1;

    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        while(1==1){
            service1.produce();
        }
    }

}

消费者线程:

package ReentrantLock_part1;

public class Thread2 extends Thread{
    private Service1 service1;

    public Thread2(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        while(1==1){
            service1.custom();
        }
    }

}

业务类:

package ReentrantLock_part1;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Service1 {
    volatile private boolean flag = false;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void produce(){
        try {
            lock.lock();            
            while(flag == false){
                condition.await();
            }
            flag = false;
            System.out.println("****");
            condition.signal();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
    }
    public void custom(){
        try {
            lock.lock();            
            while(flag == true){
                condition.await();
            }
            flag = true;
            System.out.println("====");
            condition.signal();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
    }
}

部分运行结果:

====
****
====
****
====
****
====
****
====

验证多对多生产者消费者模式

同synchronized下的多对多。使用signalAll()方法避免假死,使用volatile关键字达到可见性目的(确保boolean量及时更新到各个线程副本中)。
工程代码如下:
运行类:

package ReentrantLock_part1;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Service1 service1 = new Service1();
        Thread1[] thread1s = new Thread1[10];
        Thread2[] thread2s = new Thread2[10];
        for(int i=0;i<10;i++){
            thread1s[i] = new Thread1(service1);
            thread1s[i].start();
            thread2s[i] = new Thread2(service1);
            thread2s[i].start();
        }
    }
}

生产者线程:

package ReentrantLock_part1;

public class Thread1 extends Thread{
    private Service1 service1;

    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        while(1==1){
            service1.produce();
        }
    }

}

消费者线程类:

package ReentrantLock_part1;

public class Thread2 extends Thread{
    private Service1 service1;

    public Thread2(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        while(1==1){
            service1.custom();
        }
    }

}

业务层类:

package ReentrantLock_part1;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Service1 {
    volatile private boolean flag = false;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void produce(){
        try {
            lock.lock();            
            while(flag == false){
                condition.await();
            }
            flag = false;
            System.out.println("****");
            condition.signalAll();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
    }
    public void custom(){
        try {
            lock.lock();            
            while(flag == true){
                condition.await();
            }
            flag = true;
            System.out.println("====");
            condition.signalAll();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
    }
}

部分运行结果如下:

====
****
====
****
====
****
====
****
====

“公平锁” 和 “非公平锁”

公平锁:多线程获取锁的顺序是按照线程加锁的顺序来分配的。
非公平锁:都是随机,可能造成某些线程一直无法运行。
如何实现:
在业务类中,生成ReentrantLock对象时,通过其参数boolean值决定是公平锁还是非公平锁。

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

其它方法

getHoldCount()

查询当前以lock对象为对象监视器的线程数量。
使用方法:int num = lock.getHoldCount();

getQueueLength

查询当前等待lock锁权限的线程数(排除await状态的)
使用方法:int length = lock.getQueueLength();

getWaitQueueLength(Condition condition)

查询当前lock监视器下处于await状态的线程数量
使用方法:int length = lock.getWaitQueueLength(Condition condition)

hasQueuedThread(Thread thread)

查询指定线程是否处于等待锁的状态
使用方法:boolean isWait = lock.hasQueueThread(Thread thread)

hasQueuedThreads()

查询是否存在线程等待当前锁对象
使用方法:boolean isWait = lock.hasQueueThreads()

hasWaiters(Condition condition)

查询是否有线程等待当前锁的condition对象
使用方法:boolean hasWait = lock.hasWaiters(Condition condition)

isFair()

判断当前lock对象是否为公平锁
使用方法:boolean isfair = lock.isFair();

isHeldByCurrentThread()

判断当前线程是否以该锁对象为对象监视器。
使用方法:boolean isHeld = lock.isHeldByCurrentThread();

isLocked()

查询该锁对象是否处于锁定状态
使用方法:boolean isLocked = lock.isLocked();

lockInterruptibly()

如果当前线程未被中断,则获取此锁定,若已经中断则抛出异常。

tryLock()

当前锁未被线程锁定时,才可以被其他线程锁定。

tryLock(long time,TimeUnit unit)

给定int+时间单位的参数,即在该时间内,该锁未被其他线程锁定,则可以被当前线程锁定。


ReentrantReadWriteLock(读读共享)

多个读锁之间不互斥,读锁和写锁之间互斥,写锁与写锁之间也互斥
及,多个读操作可异步执行,读写操作须同步执行,写写操作也须同步执行。


读读共享案例

工程代码如下
运行类

package ReadLock;

public class Test {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        Thread1 thread1 = new Thread1(service1);
        Thread2 thread2 = new Thread2(service1);
        thread1.setName("AAA");
        thread2.setName("BBB");
        thread1.start();
        thread2.start();
    }
}

线程类1

package ReadLock;

public class Thread1 extends Thread{
    private Service1 service1;

    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.doReads();
    }
}

线程类2

package ReadLock;

public class Thread2 extends Thread{
    private Service1 service1;

    public Thread2(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.doReads();
    }

}

逻辑类

package ReadLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service1 {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void doReads(){
        try {
            System.out.println(Thread.currentThread().getName()+" start read at "+System.currentTimeMillis()/1000);
            lock.readLock().lock();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+" stop read at "+System.currentTimeMillis()/1000);
        } catch (Exception e) {
            // TODO: handle exception
        }
        lock.readLock().unlock();
    }
}

运行结果

BBB start read at 1526609034
AAA start read at 1526609034
AAA stop read at 1526609036
BBB stop read at 1526609036

如上,开启两个线程同时去争抢锁,发现当锁为读锁时,两线程运行逻辑的时间时几乎一致的,即两者时异步执行的。提高了运行的效率


读写互斥案例

工程代码如下
运行类

package ReadWriteLock;

public class Test {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        Thread1 thread1 = new Thread1(service1);
        Thread2 thread2 = new Thread2(service1);
        thread1.setName("AAA");
        thread2.setName("BBB");
        thread1.start();
        thread2.start();
    }
}

读线程

package ReadWriteLock;

public class Thread1 extends Thread{
    private Service1 service1;

    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.doReads();
    }
}

写线程

package ReadWriteLock;

public class Thread2 extends Thread{
    private Service1 service1;

    public Thread2(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.doWrites();
    }

}

逻辑代码

package ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service1 {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void doReads(){
        try {
            System.out.println(Thread.currentThread().getName()+" start read at "+System.currentTimeMillis()/1000);
            lock.readLock().lock();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+" stop read at "+System.currentTimeMillis()/1000);
        } catch (Exception e) {
            // TODO: handle exception
        }
        lock.readLock().unlock();
    }
    public void doWrites(){
        try {
            System.out.println(Thread.currentThread().getName()+" start write at "+System.currentTimeMillis()/1000);
            lock.writeLock().lock();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+" stop write at "+System.currentTimeMillis()/1000);
        } catch (Exception e) {
            // TODO: handle exception
        }
        lock.writeLock().unlock();
    }
}

运行结果

AAA start read at 1526609551
BBB start write at 1526609551
AAA stop read at 1526609553
BBB stop write at 1526609555

从结果看,读线程和写线程时同步的,说明读写是互斥的。


写写互斥案例

工程代码如下
运行类

package WriteLock;

public class Test {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        Thread1 thread1 = new Thread1(service1);
        Thread2 thread2 = new Thread2(service1);
        thread1.setName("AAA");
        thread2.setName("BBB");
        thread1.start();
        thread2.start();
    }
}

线程类1

package WriteLock;

public class Thread1 extends Thread{
    private Service1 service1;

    public Thread1(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.doWrites();
    }
}

线程类2

package WriteLock;

public class Thread2 extends Thread{
    private Service1 service1;

    public Thread2(Service1 service1) {
        super();
        this.service1 = service1;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        service1.doWrites();
    }

}

逻辑代码

package WriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service1 {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void doWrites(){
        try {
            System.out.println(Thread.currentThread().getName()+" start write at "+System.currentTimeMillis()/1000);
            lock.writeLock().lock();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+" stop write at "+System.currentTimeMillis()/1000);
        } catch (Exception e) {
            // TODO: handle exception
        }
        lock.writeLock().unlock();
    }
}

运行结果

AAA start write at 1526609743
BBB start write at 1526609743
AAA stop write at 1526609745
BBB stop write at 1526609747

由结果看,写写操作也是互斥的。


Lock可视为synchronized关键字的进阶。将其替换出来,且具有synchronized关键字不具备的功能,包括Condition对象带来的等待/唤醒时的选择性唤醒,以及读写锁使用以提升操作效率等。

猜你喜欢

转载自blog.csdn.net/weixin_39389850/article/details/80350990