进程的不安全问题与解决方案

不安全的买票

sleep会放大问题的发生性噢!

package syn;

/**
 * 不安全的买票
 */
public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"你").start();
        new Thread(buyTicket,"我").start();
        new Thread(buyTicket,"他").start();

    }


}
class BuyTicket implements Runnable{

    //票
    private int ticketNums= 10;
    boolean flag=true;//外部停止方式

    @Override
    public void run() {
        //买票
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private  void buy() throws InterruptedException {
        //判断是否有票
        if(ticketNums<=0){
            flag=false;
            return;
        }
        Thread.sleep(1000);
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}

运行代码我们会发现,7这张票被拿到了两次,这很明显的是不被允许的吧
在这里插入图片描述
所以为了解决这个问题,我们需要用到线程锁这个概念,即使用synchronized关键词

在并发编程中存在线程安全问题,主要原因有:
1.存在共享数据
2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性

synchronized的三种应用方式
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

那么我们就可以开始解决上面的问题啦:
在这里插入图片描述
只需要在买票方法buy前面加上锁就可以解决啦

不安全的取钱

package syn;

/**
 * 不安全的取钱
 * 两个人去银行取钱,账户
 */
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100, "结婚基金");

        Drawing you = new Drawing(account, 50, "你");
        Drawing girl = new Drawing(account, 100, "girl");

        you.start();
        girl.start();

    }
}

//账户
class Account {
    int money;//余额

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }

    String name;//卡命
}


//银行,模拟取款
class Drawing extends Thread {
    Account account;
    //取了多少钱
    int drawingMoney;
    //现在手里有多少钱
    int nowMoney;


    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
//        this.setName(name);
        this.account = account;
        this.drawingMoney = drawingMoney;

    }

    @Override
    public void run() {
        synchronized (account) {
            //判断现在有没有钱
            if (account.money - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "钱不够");
                return;
            }
            //sleep可以方法问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //卡内余额
            account.money = account.money - drawingMoney;
            //你手里的前
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name + "余额为" + account.money);
            System.out.println(this.getName() + "手里的前" + nowMoney);
        }
    }
}

这里采用的加锁就是代码块的加锁方式

不安全的集合

这里我使用了lambda表达式,前面的文章我有专门细说的使用方法的噢!

package syn;

import java.util.ArrayList;

/**
 * 线程不安全的集合
 */
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            }).start();

        }
        Thread.sleep(1000);
        System.out.println(list.size());
    }
}

代码看似没毛病对吧,但是如果去掉锁运行就会发现结果有问题
这里如果按照理想来说,应该输出是10000的
在这里插入图片描述
因为CPU的执行是很快的,在某一时刻内,可能就有两条线程同时操作了同一个list,所以才会造成这种结果

进程死锁问题

package syn;

/**
 * 进程死锁
 */
public class DeadLock {
    public static void main(String[] args) {
        Makeup makeup1=new Makeup(0,"白雪公主");
        Makeup makeup2=new Makeup(1,"灰姑凉");

        makeup1.start();
        makeup2.start();
    }
}


//口红
class Lipstick {

}

//镜子
class Mirror {

}


class Makeup extends Thread {
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

     int choose;//选择   这里千万不能加上static
    //因为如果他是一个静态变量的话,进程是不会发生死锁的
    String girlname;//选择化妆品的人


    public Makeup(int choose, String girlname) {
        this.choose = choose;
        this.girlname = girlname;
    }

    @Override
    public void run() {
        try {
            makeup(choose);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void makeup(int choose) throws InterruptedException {
        if (choose == 0) {
            synchronized (lipstick) {
                System.out.println(girlname + "在用口红");
                Thread.sleep(1000);

            synchronized (mirror) {
                System.out.println(girlname + "在用镜子");
            }

            }
        } else {
            synchronized (mirror) {
                System.out.println(girlname + "在用镜子");
                Thread.sleep(1000);

            synchronized (lipstick) {
                System.out.println(girlname + "在用口红");
            }
            }
        }

    }
}

运行后发现进程不会停止,这是因为我们的两个线程都在等待对方线程执行完毕,但是每一条线程里面都被我们给上了锁(嵌套锁)

所以解决方案当然就是不要拆解掉嵌套锁啦
在这里插入图片描述
嵌套锁的方式是非常不可取的,使用时一定要摸清情况,避免发生死锁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值