多线程-Lock的使用

1,使用ReentrantLock类

​ 在java中,可以使用synchronized关键字可以实现线程之间同步互斥,在jdk1.5中新增了ReentrantLock类也能够达到同样的效果,并且在扩展功能上面也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加灵活。

使用ReentrantLock来实现线程同步

示例:创建一个共享对象MyService,将这个对象也是使用lock来锁住,只有拿到这个对象的线程才能够来操作这个资源中的代码,记住,lock是不会自己释放锁的,因此在代码执行完成以后是需要手动释放锁的。

class MyService{
    private Lock lock = new ReentrantLock();

    public void testMethod(){
        //获得锁
        lock.lock();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() +"==>"+ (i));
        }
        //释放锁
        lock.unlock();
    }
}

class MyThread extends Thread{
    private MyService myService;
    public MyThread(MyService myService){
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.testMethod();
    }
}
public class Test1 {
    public static void main(String[] args) {
        MyService myService = new MyService();
        new MyThread(myService).start();
        new MyThread(myService).start();
        new MyThread(myService).start();
        new MyThread(myService).start();
        new MyThread(myService).start();
    }
}

2,使用Condition实现等待/通知

​ 当一个线程获取到锁了以后就可以执行同步代码块中的代码了,当这个线程执行完成以后会释放锁,这个时候主线程会休眠3秒钟,然后也会来执行同步代码块中的方法。

Object类Condition类
wait()await()
wait(long timeout)await(long timeout,TimeUnit unit)
notify()signal()
notifyAll()signalAll()
class MyService1{
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void await(){
        try{
            lock.lock();
            System.out.println("await时间为 " + System.currentTimeMillis());
            condition.await();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signal(){
        try {
            lock.lock();
            System.out.println("signal时间为 " + System.currentTimeMillis());
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}

class ThreadA extends Thread{
    private MyService1 myService1;
    public ThreadA(MyService1 myService1){
        this.myService1 = myService1;
    }

    @Override
    public void run() {
        myService1.await();
    }
}
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        MyService1 myService1 = new MyService1();
        new ThreadA(myService1).start();
        Thread.sleep(3000);
        myService1.signal();
    }
}

3,生产者/消费者模式

实现一对一交替打印

class MyService3{
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;

    public void set(){
        try {
            lock.lock();
            while (hasValue){
                condition.await();
            }
            System.out.println("现在正在设置值");
            hasValue = true;
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void get(){
        try {
            lock.lock();
            while (!hasValue){
                condition.await();
            }
            System.out.println("现在正在获取值");
            hasValue = false;
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

class MyThreadA extends Thread{
    private MyService3 myService3;
    public MyThreadA(MyService3 myService3){
        this.myService3 = myService3;
    }

    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            myService3.set();
        }
    }
}

class MyThreadB extends Thread{
    private MyService3 myService3;
    public MyThreadB(MyService3 myService3){
        this.myService3 = myService3;
    }

    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            myService3.get();
        }
    }
}


public class Test3 {
    public static void main(String[] args) {
        MyService3 myService3 = new MyService3();
        new MyThreadA(myService3).start();
        new MyThreadB(myService3).start();
    }
}

4,公平锁与非公平锁

​ 公平与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序按照线程加锁的顺序来分配的(即先来先得的FIFO先进先出顺序),而非公平锁就是一种锁的抢占机制,是随机获得锁的,这种方式可能造成某些线程一直拿不到锁。

公平锁的实现:

class MyService4{
    private ReentrantLock lock;

    public MyService4(boolean isFair){
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "获得到锁");
        }finally {
            lock.unlock();
        }
    }
}
public class Test4 {
    public static void main(String[] args) {
        final MyService4 service4 = new MyService4(true);
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName()+"运行了");
            service4.serviceMethod();
        };
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(runnable);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

非公平锁:表示先启动的线程不一定先获取到锁。

class MyService4{
    private ReentrantLock lock;

    public MyService4(boolean isFair){
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "获得到锁");
        }finally {
            lock.unlock();
        }
    }
}
public class Test4 {
    public static void main(String[] args) {
        final MyService4 service4 = new MyService4(false);
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName()+"运行了");
            service4.serviceMethod();
        };
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(runnable);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

5,synchronized和ReentrantLock的区别

两者的共同点:

  1. 都是用来协调多线程对共享对象、变量的访问
  2. 都是可重入锁,同一线程可以多次获得同一个锁
  3. 都保证了可见性和互斥性

两者的不同点:

  1. ReentrantLock显示获得释放锁;synchronized隐式获得释放锁
  2. ReentrantLock可相应中断,可轮回;synchronized是不可以相应中断的
  3. ReentrantLock是API级别的;synchronized是JVM级别的
  4. ReentrantLock可以实现公平锁(默认是非公平锁);synchronized只能是非公平锁
  5. ReentrantLock是同步非阻塞,采用乐观并发策略;synchronized是同步阻塞,采用悲观并发策略
  6. ReentrantLock是一个接口;synchronized时java关键字
  7. ReentrantLoc在发生异常的时候不会自动释放锁;synchronized在发生异常的时候会自动释放锁
  8. ReentrantLock可以响应中断;synchronized不能响应中断,只能一直等待直到执行结束
  9. ReentrantLock无法知道是否成功获取到锁;synchronized可以知道是否成功获取到锁
  10. ReentrantLock通过读写锁提高效率;synchronized在任何情况下都只有一个线程可以访问这个资源

6,使用ReentrantReadWriteLock类

​ 类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后后面的任务,这样可以保证线程的安全,但是这样的效率是特别低的。因此引进ReentrantReadWriteLock类,该类使用了读写锁(读锁也叫共享锁,写锁也叫排他锁),读锁之间是不互斥的,读锁和写锁之间是互斥的,写锁和写锁之间是互斥的,也就说同一时刻可以有多个线程获取到读锁来读取数据。

读锁之间的共享:同一时刻多个线程都是可以获取到这一把读锁的。

class MyService5{
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){
        try {
            try {
                lock.readLock().lock();
                System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到读锁");
                Thread.sleep(10000);
            }finally {
                lock.readLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadC extends Thread{
    private MyService5 myService5;

    public ThreadC(MyService5 myService5){
        this.myService5 = myService5;
    }

    @Override
    public void run() {
        myService5.read();
    }
}

class ThreadD extends Thread{
    private MyService5 myService5;

    public ThreadD(MyService5 myService5){
        this.myService5 = myService5;
    }

    @Override
    public void run() {
        myService5.read();
    }
}


public class Test5 {
    public static void main(String[] args) {
        MyService5 myService5 = new MyService5();
        ThreadC threadC = new ThreadC(myService5);
        threadC.setName("C");
        ThreadD threadD = new ThreadD(myService5);
        threadD.setName("D");
        threadC.start();
        threadD.start();
    }
}

读锁和写锁之间的互斥:同一时刻只有一个线程都是可以获取到这一把写锁的,在这个时候读的时候就不能写,写的时候就不能读

class MyService5{
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){
        try {
            try {
                lock.readLock().lock();
                System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到读锁");
                Thread.sleep(10000);
            }finally {
                lock.readLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public void write(){
        try {
            try {
                lock.writeLock().lock();
                System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到写锁");
                Thread.sleep(10000);
            }finally {
                lock.writeLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadC extends Thread{
    private MyService5 myService5;

    public ThreadC(MyService5 myService5){
        this.myService5 = myService5;
    }

    @Override
    public void run() {
        myService5.read();
    }
}

class ThreadD extends Thread{
    private MyService5 myService5;

    public ThreadD(MyService5 myService5){
        this.myService5 = myService5;
    }

    @Override
    public void run() {
        myService5.write();
    }
}


public class Test5 {
    public static void main(String[] args) throws InterruptedException {
        MyService5 myService5 = new MyService5();
        ThreadC threadC = new ThreadC(myService5);
        threadC.setName("C");
        threadC.start();
        Thread.sleep(1000);
        ThreadD threadD = new ThreadD(myService5);
        threadD.setName("D");
        threadD.start();
    }
}

写锁之间的互斥:同一时刻只有一个线程都是可以获取到这一把写锁的。

class MyService5{
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void write(){
        try {
            try {
                lock.writeLock().lock();
                System.out.println(Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "获取到写锁");
                Thread.sleep(10000);
            }finally {
                lock.writeLock().unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadC extends Thread{
    private MyService5 myService5;

    public ThreadC(MyService5 myService5){
        this.myService5 = myService5;
    }

    @Override
    public void run() {
        myService5.read();
    }
}

class ThreadD extends Thread{
    private MyService5 myService5;

    public ThreadD(MyService5 myService5){
        this.myService5 = myService5;
    }

    @Override
    public void run() {
        myService5.read();
    }
}


public class Test5 {
    public static void main(String[] args) {
        MyService5 myService5 = new MyService5();
        ThreadC threadC = new ThreadC(myService5);
        threadC.setName("C");
        ThreadD threadD = new ThreadD(myService5);
        threadD.setName("D");
        threadC.start();
        threadD.start();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值