大白话理解,快速拿下JUC(二)

文章详细介绍了Java中的同步机制,包括synchronized关键字的四种使用方式以及Lock接口的可重入锁特性。对比了synchronized和Lock在异常处理、响应中断、锁获取状态等方面的不同。通过交替计数和解决虚假唤醒问题的示例,展示了如何使用synchronized和Lock实现线程间的同步和通信。
摘要由CSDN通过智能技术生成

1、Synchronized关键字学习       

        synchronized 是 Java 中的关键字,是一种同步锁。它修饰的对象有以下几种:

        a. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}
括起来的代码,作用的对象是调用这个代码块的对象;
        b. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用
的对象是调用这个方法的对象;
o 虽然可以使用 synchronized 来定义方法,但 synchronized 并不属于方法定
义的一部分,因此,synchronized 关键字不能被继承。如果在父类中的某个方
法使用了 synchronized 关键字,而在子类中覆盖了这个方法,在子类中的这
个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上
synchronized 关键字才可以。当然,还可以在子类方法中调用父类中相应的方
法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,
子类的方法也就相当于同步了。
        c. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的
所有对象;
        d. 修改一个类,其作用的范围是 synchronized 后面括号括起来的部分,作用主
的对象是这个类的所有对象。
public class sync {
    public static void main(String[] args) {
        piao p=new piao();
        new Thread(() -> {
            while (true){
                p.mai();
            }
        }, "A").start();
        new Thread(() -> {
            while (true){
                p.mai();
            }
        }, "B").start();
        new Thread(() -> {
            while (true){
                p.mai();
            }
        }, "C").start();

    }
}
class piao{
    private int num=30;

    public synchronized void mai(){
        if(num>0){
            num--;
            System.out.println(Thread.currentThread().getName()+"卖出了一张票,剩余:"+num);
        }
    }
}

 2、Lock 可重入锁学习        

       a、 Lock 不是 Java 语言内置的,synchronized 是 Java 语言的关键字,因此是内 置特性。Lock 是一个类,通过这个类可以实现同步访问;
        b、Lock 和 synchronized 有一点非常大的不同,采用 synchronized 不需要用户 去手动释放锁,当 synchronized 方法或者 synchronized 代码块执行完之后, 系统会自动让线程释放对锁的占用;而 Lock 则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
补充:线程创建后,执行start()线程不一定会马上创建,会由操作系统决定是否创建,比如系统正在处理一些其余的东西,比较忙,就会过一会创建线程
import java.util.concurrent.locks.ReentrantLock;

public class lock {
    public static void main(String[] args) {
        lockPiao p=new lockPiao();
        new Thread(() -> {
            while (true){
                p.mai();
            }
        }, "A").start();
        new Thread(() -> {
            while (true){
                p.mai();
            }
        }, "B").start();
        new Thread(() -> {
            while (true){
                p.mai();
            }
        }, "C").start();
    }
}
class lockPiao{
    //余票数量
    int piao=30;
    private final ReentrantLock lock=new ReentrantLock();
    public void mai(){
        //上锁
        lock.lock();
        try {
            if(piao>0){
                piao--;
                System.out.println(Thread.currentThread().getName()+"卖出了一张票,剩余:"+piao);
            }
        }catch (Exception e){
            System.out.println("报错了");
        }
        finally {
            //解锁
            lock.unlock();
        }        
    }
}

3、Lock 和 synchronized 有以下几点不同:

        a. Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内 置的语言实现;
        b. synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现 象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很 可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;
        c. Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断;
        d. 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
        e. Lock 可以提高多个线程进行读操作的效率。
4、多线程通知
        多线程常规操作总共分为以下几步

 5、synchronized进行交替计数小案例

public class sync01 {
    public static void main(String[] args) {
        jishu j=new jishu();
        new Thread(()->{
            for (int i = 0; i <=10; i++) {
                try {
                    j.add();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <=10; i++) {
                try {
                    j.del();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class jishu{
    //1、数量资源
    int number=0;

    //2、每次加一方法
    public synchronized void add() throws InterruptedException {
        //基本格式  判断  干活 通知
        //判断
        if(number!=0){
            this.wait();
        }
        //干活
        number++;
        System.out.println(Thread.currentThread().getName()+"进行操作,数字目前为:"+number);
        //通知
        this.notifyAll();
    }
    //2、每次减一方法
    public synchronized void del() throws InterruptedException {
        //基本格式  判断  干活 通知
        //判断
        if(number!=1){
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName()+"进行操作,数字目前为:"+number);
        //通知
        this.notifyAll();
    }
}

6、多线程虚假唤醒问题

public class sync01 {
    public static void main(String[] args) {
        jishu j=new jishu();
        new Thread(()->{
            for (int i = 0; i <=10; i++) {
                try {
                    j.add();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <=10; i++) {
                try {
                    j.del();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <=10; i++) {
                try {
                    j.del();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i <=10; i++) {
                try {
                    j.del();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class jishu{
    //1、数量资源
    int number=0;

    //2、每次加一方法
    public synchronized void add() throws InterruptedException {
        //基本格式  判断  干活 通知
        //判断
        if(number!=0){
            this.wait();
        }
        //干活
        number++;
        System.out.println(Thread.currentThread().getName()+"进行操作,数字目前为:"+number);
        //通知
        this.notifyAll();
    }
    //2、每次减一方法
    public synchronized void del() throws InterruptedException {
        //基本格式  判断  干活 通知
        //判断
        if(number!=1){
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName()+"进行操作,数字目前为:"+number);
        //通知
        this.notifyAll();
    }
}

运行结果如下:

此问题就是虚假唤醒问题,原因分析为:比如线程B先得到,到判断时发现不是1,那么进入wait等待状态,线程B释放了锁,那么线程A、C、D都有可能拿到锁,比如下一次是D拿到了锁,那么其判断又是不是1,又进入wait等待,下次A或者C拿到之后,不满足条件,进行了++后唤醒其他线程,那么B和C都唤醒之后会遵循线程等待特点(在哪里睡,就在那里醒),那么B线程则会去执行--操作,D也会进行--操作,如下图所示,该代码会在64行唤醒,然后继续往下执行去执行67行

 未避免此虚假唤醒,官方给出方法,将if判断改为while循环,此方法好比坐飞机,人员都坐上飞机后,机长广播说有不明物带上飞机,全体乘客下机检查,待检查完后,所有人需要再次过安检才能登机是一个道理,此例子希望可以助你理解问题

7、使用lock完成交替计算

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

public class lockLock {
    public static void main(String[] args) {
        lock22 lock22=new lock22();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                lock22.add();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                lock22.del();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                lock22.add();
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                lock22.del();
            }
        },"D").start();
    }
}
class lock22{
    private  int num =0;

    Lock lock=new ReentrantLock();
    Condition condition = lock.newCondition();

    //++
    public void add(){
        lock.lock();
        try {
            while (num!=0){
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"操作:"+num);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    //--
    public void del(){
        lock.lock();
        try {
            while (num!=1){
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"操作:"+num);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值