Java 解决线程安全问题:上锁

为什么会有线程安全的问题?

比如两个人同时去做同一件事情,会很快,但是呢,因为要做同一件事,很有可能就会发生争抢冲突,导致这个事情被玩坏

我这里举了一个例子,我和我女朋友(存在的)一起洗碗
我和我女朋友各自是一个线程,对碗进行洗碗操作

代码示例:
首先是创建一个堆没洗的碗,还有洗碗的方法

package com.kaikeba.demo;
import java.util.concurrent.locks.ReentrantLock;

public class Bowl{
    //碗数量
    private int bowlNumber;

	//一参构造,用于创建自定义 碗数量 的一堆碗对象
    public Bowl(int bowlNumber) {
        this.bowlNumber = bowlNumber;
    }
	//返回当前碗的数量
    public int getBowlNumber() {
        return bowlNumber;
    }

    //洗碗方法
    public void wash(){
        
            //循环直到碗洗碗
            while (this.getBowlNumber()>0) {
                try {
                    //延迟,放大差错
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                bowlNumber--;
                System.out.println(Thread.currentThread().getName() + ",还有" + bowlNumber + "个碗");
        }

    }
}

这是我洗碗的线程

public class Me implements Runnable{
    //传过来这堆还没洗的碗对象
    private Bowl b = new Bowl();

    public Me( Bowl b) {
        this.b = b;
    }

    @Override
    public void run() {
        //我把碗洗了
        b.wash();
        
    }
}

这是叫女朋友洗碗的线程

package com.kaikeba.demo;

public class She implements Runnable{
    private Bowl b = new Bowl();

    //传过来这堆还没洗的碗对象
    public She(Bowl b) {
        this.b = b;
    }
//女朋友洗碗
    @Override
    public void run() {
        b.wash();

    }
}

然后我们把饭吃掉,生成一堆没洗的碗,我叫上我的女朋友(情愿)一起去洗碗

public class StartWash {
    public static void main(String[] args) {
        //把饭吃了,留下一堆需要洗的碗数量
        Bowl bowl = new Bowl(10);
        //我出现
        Me me = new Me(bowl);
        //叫上女朋友
        She she = new She(bowl);
        //开始同时洗碗
        //修改一下线程的名字
        new Thread(me,"我洗了一个碗").start();
        new Thread(she,"女朋友洗了一个碗").start();
    }
}

结果很可怕
女朋友很想帮我洗碗,我们就争抢起来了,最后洗完还凭空多洗了一个???(邻居正在吃饭,手里的碗突然消失:卧槽我碗呢?)

//运行结果
女朋友洗了一个碗:
,还有8个碗
我洗了一个碗:
,还有9个碗
我洗了一个碗:
,还有6个碗
女朋友洗了一个碗:
,还有6个碗
女朋友洗了一个碗:
,还有5个碗
我洗了一个碗:
,还有5个碗
女朋友洗了一个碗:
,还有4个碗
我洗了一个碗:
,还有3个碗
女朋友洗了一个碗:
,还有2个碗
我洗了一个碗:
,还有1个碗
女朋友洗了一个碗:
,还有-1个碗
我洗了一个碗:
,还有0个碗

Process finished with exit code 0

这时候我们提前就要好好沟通,我们一个一个的洗,洗完了一个才可以洗下一个喔!
方案1:同步代码块

使用 synchronized() 对需要完整执行的语句进行“包裹”,就是洗碗的方法,synchronized(Obj obj) 构造方法里是可以传入任何类的对象,

但是既然是监听器就传一个唯一的对象来保证“锁”的唯一性,因此一般使用共享资源的对象来作为 obj 传入 synchronized(Obj obj) 里:

public class Bowl{
    //碗数量
    private int bowlNumber;

    public Bowl(int bowlNumber) {
        this.bowlNumber = bowlNumber;
    }

    public int getBowlNumber() {
        return bowlNumber;
    }

    public Bowl() {
    }

    //洗碗方法
    public  void wash(){
		//synchronized 同步代码块
        synchronized (this){
            //循环直到碗洗碗
            while (this.getBowlNumber()>0) {
                try {
                    //延迟s,放大差错
                    Thread.sleep(250);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                bowlNumber--;
                System.out.println(Thread.currentThread().getName() + ":\n,还有" + bowlNumber + "个碗");
            }
        }

    }
}

方案2:同步方法
在方法的申明里申明 synchronized 即可:

public class Bowl{
    //碗数量
    private int bowlNumber;
    
    public Bowl(int bowlNumber) {
        this.bowlNumber = bowlNumber;
    }

    public int getBowlNumber() {
        return bowlNumber;
    }

    public Bowl() {
    }

    //洗碗方法
    public synchronized void wash(){
        
        //循环直到碗洗完
        while (this.getBowlNumber()>0) {
            try {
                //延迟,放大差错
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            bowlNumber--;
            System.out.println(Thread.currentThread().getName()+":\n,还有"+bowlNumber+"个碗");

        }
    }
}

方法3: 使用同步锁:

account 类创建私有的 ReetrantLock 对象,调用 lock() 方法,同步执行体执行完毕之后,需要用 unlock() 释放锁。

package com.kaikeba.demo;

import java.util.concurrent.locks.ReentrantLock;

public class Bowl{
    //碗数量
    private int bowlNumber;
    private ReentrantLock lock = new ReentrantLock();

    public Bowl(int bowlNumber) {
        this.bowlNumber = bowlNumber;
    }

    public int getBowlNumber() {
        return bowlNumber;
    }

    public Bowl() {
    }

    //洗碗方法
    public void wash(){

        //显式锁,上锁
        lock.lock();
        //循环直到碗洗碗
        while (this.getBowlNumber()>0) {
            try {
                //延迟,放大差错
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            bowlNumber--;
            System.out.println(Thread.currentThread().getName()+":\n,还有"+bowlNumber+"个碗");
        }
        //开锁
        lock.unlock();
    }
}


我洗了一个碗,还有9个碗
我洗了一个碗,还有8个碗
我洗了一个碗,还有7个碗
我洗了一个碗,还有6个碗
我洗了一个碗,还有5个碗
我洗了一个碗,还有4个碗
我洗了一个碗,还有3个碗
我洗了一个碗,还有2个碗
我洗了一个碗,还有1个碗
我洗了一个碗,还有0个碗

这不就不争不抢,和平的洗完了碗。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值