线程的安全

本文讨论了Java中线程安全问题的四种常见情况:丢失修改、脏读、不可重复读,并通过示例代码详细阐述了这些现象。为了解决这些问题,文章提出了使用`synchronized`关键字来实现线程同步,确保共享资源在同一时刻只能被一个线程访问,从而提高线程安全性。
摘要由CSDN通过智能技术生成

线程安全

​ 由于系统没有为线程分配资源,它们与进程中的其他线程共享进程的共享资源,这时候如果多个线程共享一个数据,如果处理不好的话就会出现线程的安全隐患,比如丢失修改,不可重复读,读脏数据等。

1.丢失修改

package com.sw.xc;

public class Thread1 implements Runnable{
    int num = 5;//5个苹果
    @Override
    public void run() {
        while (num>0) {
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "正在卖第" + num-- + "个苹果还剩下" + num + "个苹果");
            } else {
                System.out.println("苹果卖完了");
            }
        }
    }
}

package com.sw.xc;

public class Test {
    public static void main(String[] args) {
        new Thread(new Thread1(),"t1").start();
        new Thread(new Thread1(),"t2").start();
    }
}
/*测试结果
t1正在卖第5个苹果还剩下4个苹果
t2正在卖第5个苹果还剩下4个苹果
t1正在卖第4个苹果还剩下3个苹果
t2正在卖第4个苹果还剩下3个苹果
t1正在卖第3个苹果还剩下2个苹果
t2正在卖第3个苹果还剩下2个苹果
t2正在卖第2个苹果还剩下1个苹果
t2正在卖第1个苹果还剩下0个苹果
1正在卖第2个苹果还剩下1个苹果
t1正在卖第1个苹果还剩下0个苹果*/

上面的卖苹果t1和t2都有同时在卖同一个苹果,如当t1在卖第5个苹果的时候还没来得及去修改苹果的数量,t2就以及开始卖苹果了而且也是卖的第五个,这样的线程是不安全的,这就造成了修改丢失的情况。

2.脏读

package com.sw.xc;

public class DirtyRead {
    private String userName = "abc";
    private String passWord = "321";

    public synchronized void setValue(String userName,String passWord){
        this.userName = userName;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        this.passWord = passWord;

        System.out.println("setValue最终结果是:userName = "+userName+",passWord = "+passWord);
    }
    public void getvalue(){
        System.out.println("getvalue方法得到的是:userName = "+this.userName+",passWord = "+this.passWord);
    }

    public static void main(String[] args) throws InterruptedException {
        final DirtyRead dr = new DirtyRead();
        Thread t1 = new Thread(()->{
            dr.setValue("cba","123");
        });
        t1.start();
        Thread.sleep(1000);
        dr.getvalue();
    }
}
//测试结果
//getvalue方法得到的是:userName = cba,passWord = 321
//setValue最终结果是:userName = cba,passWord = 123

上面get/set方法的共享资源是userName 和passWord,t1线程设置它们需要2秒,main线程获取它们只需要1秒,它们同去访问userName 和passWord,t1还没有set完main就get了它们导致最后的passWord不一样。

3.不可重复读

package com.sw.xc;

public class Person {
    int age =40;

    public synchronized  int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


package com.sw.xc;

public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        new Thread(()-> {for(int i=0;i<5;i++) {
            System.out.println("age=" + p.getAge());
        }
        },"t1").start();
        new Thread(()->{p.setAge(30);},"t2").start();
    }
}
//测试结果
//age=40
//age=30
//age=30
//age=30
//age=30

上面t1线程多次读取age,本来每次得到结果应该是一样的,但是结果不一,是由于被t2线程给中途修改了,这就是不可重复读即前后读取数据不一致。

4.解决方法

解决线程不安全的问题,我们可以使用同步关键字synchronized修饰访问共享资源的方法,被它修饰的方法同一时刻只能由一个线程进行访问,其他线程只能等已启动的线程执行完,这就很大程度的保证了线程的安全性,它也可以用来修饰类,表明该类中的所有方法都是synchronized的。它还可以修饰一段代码synchronized(this) {//代码}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值