java并发安全问题回答下

下面这段代码有没有安全问题,测试了半天和我想法不太一样,还是我没有测出来

public class Test {
    public static void main(String[] args) {
        Table table=new Table();
        Thread t1 = new Thread(){
            public void run(){
                while(true){
                    int bean = table.getBean();
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        Thread t2 = new Thread(){
            public void run(){
                while(true){
                    int bean = table.getBean();
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        t1.start();
        t2.start();
    }
}
class Table{

    User user=new User();

    public  int getBean(){
        return user.getBean();
    }
}

class User{
    private int beans = 20;

    public int getBean(){
        if(beans==0){
            throw new RuntimeException("没有豆子了!");
        }
        Thread.yield();
        return beans--;
    }
}

这段代码可能存在并发安全问题。具体来说,如果多个线程同时执行 getBean() 方法,那么可能会出现以下两个问题:

1.竞态条件:如果多个线程同时读取 beans 的值,然后将其递减并返回结果,那么可能会出现竞态条件,导致多个线程返回相同的 beans 值,而且这个值没有被正确递减。这会导致最终的结果不正确。

2.非原子操作:递减操作不是原子操作,需要多条指令执行,因此多个线程同时执行时可能会导致线程安全问题,也就是说某个线程的递减操作被其他线程所覆盖,从而导致最终结果不正确。

虽然 getBean() 方法内部有一个 Thread.yield() 方法,可能会让当前线程让出 CPU 资源,但这并不能保证安全性,因为即使当前线程让出了 CPU,其他线程仍然可能会抢占 CPU,从而导致上述问题。因此,需要对 getBean() 方法进行同步,以确保多个线程不会同时执行这个方法。

一种简单的解决方案是将 getBean() 方法声明为 synchronized 方法,这样多个线程就不能同时进入这个方法,从而避免了上述问题。修改后的代码如下:


class Table{

    User user=new User();

    public synchronized int getBean(){
        return user.getBean();
    }
}

class User{
    private int beans = 20;

    public synchronized int getBean(){
        if(beans==0){
            throw new RuntimeException("没有豆子了!");
        }
        Thread.yield();
        return beans--;
    }
}

这样就可以保证线程安全了。

Table 类中的 getBean() 方法,它们将同时访问 User 对象中的 beans 字段,从而可能导致数据竞争和并发访问的问题。
特别地,如果两个线程同时读取 beans 字段的值,然后进行自减操作并将结果写回 beans 字段,由于这是一个非原子操作,可能会出现数据不一致的情况。

解决这个问题的一种方式是在 getBean() 方法上添加 synchronized 关键字,以确保每次只有一个线程可以访问 User 对象中的 beans 字段,从而避免了数据竞争和并发访问的问题。

例如,可以将 getBean() 方法定义为以下代码:

public synchronized int getBean() {
    if (beans == 0) {
        throw new RuntimeException("没有豆子了!");
    }
    Thread.yield();
    return beans--;
}

这样做可以保证同一时刻只有一个线程可以执行 getBean() 方法,从而避免了并发访问 beans 字段的问题。

您好,我是有问必答小助手,您的问题已经有小伙伴帮您解答,感谢您对有问必答的支持与关注!
PS:问答VIP年卡 【限时加赠:IT技术图书免费领】,了解详情>>> https://vip.csdn.net/askvip?utm_source=1146287632不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^