【JAVA】多线程同步
原创地址:https://jingyan.baidu.com/article/ff411625f560de12e5823753.html
多线程对数据访问的控制及相关问题
-
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
-
一、多线程引起的数据访问安全问题
下面看一个经典的问题,银行取钱的问题:
1)、你有一张银行卡,里面有5000块钱,然后你到取款机取款,取出3000,当正在取的时候,取款机已经查询到你有5000块钱,然后正准备减去300块钱的时候
2)、你的老婆拿着那张银行卡对应的存折到银行取钱,也要取3000.然后银行的系统查询,存折账户里还有6000(因为上面钱还没扣),所以它也准备减去3000,
3)、你的卡里面减去3000,5000-3000=2000,并且你老婆的存折也是5000-3000=2000。
4)、结果,你们一共取了6000,但是卡里还剩下2000。
下面看程序的模拟过程:
package com.bjpowernode.test; public class GetMoneyTest { public static void main(String[] args) { Account account = new Account(5000); GetMoneyRun runnable = new GetMoneyRun(account); new Thread(runnable, "你").start(); new Thread(runnable, "你老婆").start(); } } // 账户Mode class Account { private int money; public Account(int money) { super(); this.money = money; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } //runnable类 class GetMoneyRun implements Runnable { private Account account; public GetMoneyRun(Account account) { this.account = account; } @Override public void run() { if (account.getMoney() > 3000) { System.out.println(Thread.currentThread().getName() + "的账户有" + account.getMoney() + "元"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } int lasetMoney=account.getMoney() - 3000; account.setMoney(lasetMoney); System.out.println(Thread.currentThread().getName() + "取出来了3000元" + Thread.currentThread().getName() + "的账户还有" + account.getMoney() + "元"); } else { System.out.println("余额不足3000" + Thread.currentThread().getName() + "的账户只有" + account.getMoney() + "元"); } } }
-
多次运行程序,可以看到有多种不同的结果,下面是其中的三种:
1. 你的账户有5000元
2. 你老婆的账户有5000元
3. 你老婆取出来了3000元你老婆的账户还有2000元
4. 你取出来了3000元你的账户还有-1000元
1. 你的账户有5000元
2. 你老婆的账户有5000元
3. 你老婆取出来了3000元你老婆的账户还有-1000元
4. 你取出来了3000元你的账户还有-1000元
1. 你的账户有5000元
2. 你老婆的账户有5000元
3. 你老婆取出来了3000元你老婆的账户还有2000元
4. 你取出来了3000元你的账户还有2000元
可以看到,由于有两个线程同时访问这个account对象,导致取钱发生的账户发生问题。当多个线程访问同一个数据的时候,非常容易引发问题。为了避免这样的事情发生,我们要保证线程同步互斥,所谓同步互斥就是:并发执行的多个线程在某一时间内只允许一个线程在执行以访问共享数据。
-
同步互斥锁 同步锁的原理:Java中每个对象都有一个内置同步锁。Java中可以使用synchronized关键字来取得一个对象的同步锁。synchronized的使用方式,是在一段代码块中,加上synchronized(object){ ... }例如,有一个show方法,里面有synchronized的代码段:
public void show() {
synchronized(object){
......
}
}
-
这其中的object可以表示任何对象,表示当前线程取得该对象的锁。一个对象只有一个锁,所以其他任何线程都不能访问该对象的所有由synchronized包括的代码段,直到该线程释放掉这个对象的同步锁(释放锁是指持锁线程退出了synchronized同步方法或代码块)。
注意:synchronized使用方式有几个要注意的地方(还是以上面的show方法举例):
①、取得同步锁的对象为this,即当前类对象,这是使用的最多的一种方式
public void show() {
synchronized(this){
......
}
}
-
将synchronized加到方法上,这叫做同步方法,相当于第一种方式的缩写
public synchronized void show() { ... }
-
静态方法的同步
public static synchronized void show() { ... }
相当于
public static void show() {
synchronized(当前类名.class)
}
相当于取得类对象的同步锁,注意它和取得一个对象的同步锁不一样
-
明白了同步锁的原理和synchronized关键字的使用,那么解决上面的取钱问题就很简单了,我们只要对run方法里面加上synchronized关键字就没有问题了,如下:
@Override public void run() { synchronized (account) { if (account.getMoney() > 3000) { System.out.println(Thread.currentThread().getName() + "的账户有" + account.getMoney() + "元"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } int lasetMoney = account.getMoney() - 3000; account.setMoney(lasetMoney); System.out.println(Thread.currentThread().getName() + "取出来了3000元" + Thread.currentThread().getName() + "的账户还有" + account.getMoney() + "元"); } else { System.out.println("余额不足3000" + Thread.currentThread().getName() + "的账户只有" + account.getMoney() + "元"); } } }
-
当甲线程执行run方法的时候,它使用synchronized (account)取得了account对象的同步锁,那么只要它没释放掉这个锁,那么当乙线程执行到run方法的时候,它就不能获得继续执行的锁,所以只能等甲线程执行完,然后释放掉锁,乙线程才能继续执行。
相关文章
- java基础知识回顾之java Thread类学习(五)--java多线程安全问题(锁)同步的前提
- Java 动态生成 复杂 .doc文件
- JAVA学习(九):JAVA多线程编程
- Java实现 LeetCode 221 最大正方形
- Java实现 LeetCode 120 三角形最小路径和
- java实现多线程(车站卖票)
- Java实现 蓝桥杯VIP 算法提高 师座操作系统
- Java实现 蓝桥杯 算法提高 上帝造题五分钟
- RDD java API使用
- java多线程 -- 同步鎖
- 【JAVA】 04-Java中的多线程
- 【JAVA】 03-Java中的异常和包的使用
- JAVA存取对象属性时,如果开程多线程,记得对相关存取方法作原子化操作定义
- JAVA实现多线程的两种方法
- java多线程(三)——锁机制synchronized(同步语句块)
- Java多线程:线程同步与关键字synchronized
- Java--使用多线程下载,断点续传技术原理(RandomAccessFile)
- java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解
- java核心知识点学习----多线程并发之线程同步
- java多线程 -- 创建线程的第三者方式 实现Callable接口
- 【JAVA】java编译错误:编码UTF8/GBK的不可映射字符
- Java多线程学习笔记 - 一、进程和线程以及实现线程的几种方法
- Java中的多线程=你只要看这一篇就够了
- paip.java 多线程参数以及返回值Future FutureTask 的使用.
- java多线程之ThreadLoal详解
- java死锁(Java-level deadlock)
- Java 递归 常见24道题目 总结
- Java学习路线-15:多线程的同步与死锁
- Java核心类库之(多线程:实现多线程、线程同步)