关于java中线程安全问题详解
一、什么时候数据在多线程并发的环境下会存在安全问题?
三个条件:
- 条件1:多线程并发。
- 条件2:有共享数据。
- 条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题。
二、怎么解决线程安全问题?
线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线程同步机制。
三、银行 取钱/存钱 案例
Account 类
package ThreadSafa;
public class Account {
// 账号
private String actno;
// 余额
private double balance;
public Account() {
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
//取款方法
public void withdraw(double money) {
// 取款之前的余额
double before = this.getBalance();
// 取款之后的余额
double after = before - money;
// 更新余额
try {
//模拟网络延时 更新余额不及时 百分百会出问题
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
AccountThread 类
package ThreadSafa;
public class AccountThread extends Thread {
// 两个线程必须共享同一个账户对象。
private Account act;
//通过构造方法传递过来账户对象
public AccountThread(Account act) {
this.act = act;
}
@Override
public void run() {
double money = 5000;
//取款
act.withdraw(5000);
System.out.println(Thread.currentThread().getName() + "账户" + act.getActno() + "取款成功,余额" + act.getBalance());
}
}
Test 类
package ThreadSafa;
public class Test {
public static void main(String[] args) {
// 创建账户对象
Account act = new Account("act-001", 10000);
//创建两个线程
Thread t1 = new AccountThread(act);
Thread t2 = new AccountThread(act);
//设置name
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
运行问题
解决方法 修改 Account 类 中的 withdraw 方法
package ThreadSafa;
public class Account {
// 账号
private String actno;
// 余额
private double balance;
public Account() {
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
//取款方法
public void withdraw(double money) {
// 以下这几行代码必须是线程排队的,不能并发
// 一个线程把这里的代码全部执行结束之后,另外一个线程才能进来
synchronized (this) {
// 取款之前的余额
double before = this.getBalance();
// 取款之后的余额
double after = before - money;
// 更新余额
try {
//模拟网络延时 更新余额不及时 百分百会出问题
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
}
为什么会出现线程安全问题
计算机系统资源分配的单位为进程,同一个进程中允许多个线程并发执行,并且多个线程会共享进程范围内的资源:例如内存地址。当多个线程并发访问同一个内存地址并且内存地址保存的值是可变的时候可能会发生线程安全问题,因此需要内存数据共享机制来保证线程安全问题。
对应到java服务来说,在虚拟中的共享内存地址是java的堆内存,比如以下程序中线程安全问题:
public class ThreadUnsafeDemo {
private static final ExecutorService EXECUTOR_SERVICE;
static {
EXECUTOR_SERVICE = new ThreadPoolExecutor(100, 100, 1000 * 10,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100), new ThreadFactory() {
private AtomicLong atomicLong = new AtomicLong(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Thread-Safe-Thread-" + atomicLong.getAndIncrement());
}
});
}
public static void main(String[] args) throws Exception {
Map<String, Integer> params = new HashMap<>();
List<Future> futureList = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
futureList.add(EXECUTOR_SERVICE.submit(new CacheOpTask(params)));
}
for (Future future : futureList) {
System.out.println("Future result:" + future.get());
}
System.out.println(params);
}
private static class CacheOpTask implements Callable<Integer> {
private Map<String, Integer> params;
CacheOpTask(Map<String, Integer> params) {
this.params = params;
}
@Override
public Integer call() {
for (int i = 0; i < 100; i++) {
int count = params.getOrDefault("count", 0);
params.put("count", ++count);
}
return params.get("count");
}
}
}
创建100个task,每个task对map中的元素累加100此,程序执行结果为:
{count=9846}
而预期的正确结果为:
{count=10000}
至于出现这种问题的原因,下面会具体分析。
判断是否有线程安全性的一个原则是:
是否有多线程访问可变的共享变量
四、总结
- 一个对象一把锁
- 线程拿到锁才能执行同步代码块代码
到此这篇关于java中线程安全问题的文章就介绍到这了,更多相关ava线程安全问题内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341