JUC并发编程基础(6)--Lock锁

LOCK接口

synchronized关键字回顾
  • 修饰一个同步语句代码块,作用范围是大括号{}括起来的代码,作用对象是调用这个代码块的对象。
  • 修饰一个方法,被修饰的方法称为同步方法,其作用范围是整个方法,作用的对象是调用这个方法的对象。
  • 修饰一个静态方法。
  • 修饰一个类。
多线程编程步骤(上)
  • 创建一个资源类,定义属性和操作方法 (高内聚低耦合思想)

  • 在资源类操作方法

    • 判断
    • 干活
    • 通知
  • 创建多个线程,调用资源类的操作方法

操作实例:

package com.amyth.JavaEE;
//创建资源类
class Ticket{
    private  int number = 30;

    //加上synchronized修饰方法
    public synchronized void sale(){
        if(number > 0 ) {
            System.out.println(Thread.currentThread().getName() + "卖出1张票,剩余:" + (number--) + "张");
        }else{
            System.out.println(Thread.currentThread().getName() + "卖出0张票,剩余:0没票啦!!!");
        }
        }
}


public class SyncTest {
    public static void main(String[] args) {
        //将资源创建出来
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i<30;i++)
                {
                    ticket.sale();
                }
            }
        },"AA").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i<40;i++)
                {
                    ticket.sale();
                }
            }
        },"BB").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i<40;i++)
                {
                    ticket.sale();
                }
            }
        },"CC").start();

    }

}
Lock和Synchronized的区别

Lock锁实现了比使用同步方法和语句可以获得更广泛的锁操作,提供了更多功能。

  • Lock不是JAVA内置的,Synchronized是关键字,有内置特性,Lock是一个类,通过这个类可以实现同步访问。
  • 最大的不同在于,Lock需要手动释放锁,否则可能会出现“死锁”现象,而Synchronized不需要,是自动释放。

利用可重入锁:ReentrantLock重写上述售票案例

package com.amyth.JavaEE;

import java.util.concurrent.locks.ReentrantLock;

//创建资源类
class Ticket{
    private  int number = 100;

    //使用可重用锁ReentrantLock
    private final ReentrantLock lock = new ReentrantLock();
    public void sale(){
        lock.lock();  //上锁
        try{
            if(number > 0 ) {
                System.out.println(Thread.currentThread().getName() + "卖出1张票,剩余:" + (number--) + "张");
            }else{
                System.out.println(Thread.currentThread().getName() + "卖出0张票,剩余:0没票啦!!!");
            }
        }finally {
            lock.unlock(); //解锁
        }
        }
}


public class SyncTest {
    public static void main(String[] args) {
        //将资源创建出来
        Ticket ticket = new Ticket();
        //这里使用了lambda表达式简化了代码
        //匿名类实现的接口使用了java.lang.FunctionalInterface注解,且只有一个待实现的抽象接口方法
        new Thread(() -> {
                for (int i=0; i<30;i++)
                {
                    ticket.sale();
                }

        },"AA").start();
        new Thread(() -> {
            for (int i=0; i<30;i++)
            {
                ticket.sale();
            }

        },"BB").start();
        new Thread(() -> {
            for (int i=0; i<30;i++)
            {
                ticket.sale();
            }

        },"CC").start();

    }

}

  • LOCK能让等待锁的线程响应中断,synchronized不行,等待的线程会一直等下去。
  • 通过LOCK可以知道是否获取锁,关键字无法做到。
  • LOCK可以提高多个线程进行读操作的效率。

总的来说,在资源竞争激烈的环境中,LOCK的性能非常非常好!

通过synchronized实现进程间通信的方法 number+1-1:

package com.amyth.JavaEE;
//资源类创建
class Share{
    private int number = 0;

    public synchronized void incr() throws InterruptedException {
        //资源类中操作方法
        if(number!=0){
            this.wait();
        }
        number++;
        System.out.println("Thread number = "+number);
        this.notify();
    }

    public synchronized void decr() throws InterruptedException {
        if (number==0){
            this.wait();
        }
        number--;
        System.out.println("Thread number = "+number);

        this.notify();
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for(int i =0;i<=10;i++){
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"ADD").start();

        new Thread(()->{
            for(int i =0;i<=10;i++){
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Decr").start();
    }
}

虚假唤醒问题实例:当线程数超过2个之后,使用if判断就可能出现虚假唤醒问题

package com.amyth.JavaEE;
//资源类创建
class Share{
    private int number = 0;

    public synchronized void incr() throws InterruptedException {
        //资源类中操作方法
        if(number!=0){
            this.wait();
        }
        number++;
        System.out.println("Thread number = "+number);
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        if (number==0){
            this.wait();
        }
        number--;
        System.out.println("Thread number = "+number);

        this.notifyAll();
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for(int i =0;i<=10;i++){
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"ADD").start();

        new Thread(()->{
            for(int i =0;i<=10;i++){
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Decr").start();

        new Thread(()->{
            for(int i =0;i<=10;i++){
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();

        new Thread(()->{
            for(int i =0;i<=10;i++){
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();
    }
}

在这里插入图片描述

此时结果中就出现1、0之外的数字,这就是虚假唤醒情况。

具体分析:

AA线程+1之后 num=1 唤醒其余线程

此时可能CC抢到了线程 通过if判断 num!=0 就进入了wait

经过一系列这种操作,一旦别的线程再次唤醒CC线程,此时就会执行wait之后的代码

因为这是基于wait方法在哪里睡着就在哪里醒来的特点,因此if判断只一次有效,第二次被唤醒就不经过if

解决办法就是使用while循环进行判断,睡着之后醒来又会在循环中再进行判断。

使用LOCK锁实现上述示例

package com.amyth.JavaEE;

import javax.naming.NameNotFoundException;
import java.util.concurrent.locks.Condition;

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

//创建资源类
class Numbers{
    private int number=0;
    private Lock lock= new ReentrantLock();
    private Condition condition = lock.newCondition();

    //+1
    public void incr() throws InterruptedException {
        lock.lock();
        //判断
        try{
            while(number !=0){
            condition.await();
        }
            //干活
            number++;
            System.out.println("number +1 :"+number);
            //通知
            condition.signalAll();

        }finally {
            lock.unlock();
        }

    }

    //-1
    public void decr() throws InterruptedException {
        lock.lock();
        //判断
        try{
            while(number ==0){
                condition.await();
            }
            //干活
            number--;
            System.out.println("number -1 :"+number);
            //通知
            condition.signalAll();

        }finally {
            lock.unlock();
        }

    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        Numbers numbers = new Numbers();
        new Thread(() -> {
            for (int i =0;i<=10;i++){
                try {
                    numbers.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

        new Thread(() -> {
            for (int i =0;i<=10;i++){
                try {
                    numbers.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值