第一种方法:同步代码块:
作用:把出现线程安全的核心代码上锁
原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行
锁对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可
缺点:会干扰其他无关线程的执行
所以,这种只是理论上的,了解即可,现实中并不会这样用
public class 多线程_4线程同步 {
public static void main(String[] args) {
//定义线程类,创建一个共享的账户对象
account a=new account("abc",10000);
//创建两个取钱的线程对象
new drawthread(a,"小明").start();
new drawthread(a,"小红").start();
}
}
//取钱的线程类
class drawthread extends Thread{
//接收处理的账户对象
private account acc;
public drawthread(account acc,String name){
super(name);
this.acc=acc;
}
public void run(){
//取钱
acc.drawmoney(10000);
}
}
class account{
private String cartId;
private double money;//账户余额
public account() {
}
public account(String cartId, double money) {
this.cartId = cartId;
this.money = money;
}
public String getCartId() {
return cartId;
}
public void setCartId(String cartId) {
this.cartId = cartId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawmoney(double money) {
//先获取是谁来取钱,线程名即是人名
String name=Thread.currentThread().getName();
//同步代码块
//作用:把出现线程安全的核心代码上锁
//原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行
//锁对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可
//缺点:会干扰其他无关线程的执行
synchronized ("遇安") {//"锁名自取,无意义"
//判断账户是否够钱
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
}
}
}
规范上:建议使用共享资源作为锁对象
对于实例化方法建议使用this作为锁对象
对于静态方法,建议使用字节码(类名.class)对象作为锁对象
//接上文代码
//实例化方法建议使用this作为锁对象
synchronized (this) {
//判断账户是否够钱
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
}
//静态方法建议使用类名.class作为锁对象
//每次只有一个线程能锁这个类,而类也是唯一的
public static void run(){
synchronized(account.class){
}
}
第二种方法:同步方法
//同步方法
public synchronized void drawmoney(double money) {
//先获取是谁来取钱,线程名即是人名
String name=Thread.currentThread().getName();
//判断账户是否够钱
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
}
那么同步代码块和同步方法哪个好一点呢?
答案是:同步代码块
因为同步代码块锁的范围更小一点,同步方法锁的范围更大一点
但其实在现实中同步方法用的更多一点,因为代码简洁好写一点,更方便
第三种方法:Lock锁
JDK5后出现,更加灵活方便
Lock是接口不能直接实例化,我们需要采用它的实现类ReentrantLock来构建Lock锁对象
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class 多线程_4线程同步Lock锁 {
public static void main(String[] args) {
//定义线程类,创建一个共享的账户对象
account a=new account("abc",10000);
//创建两个取钱的线程对象
new drawthread(a,"小明").start();
new drawthread(a,"小红").start();
}
}
//取钱的线程类
class drawthread2 extends Thread{
//接收处理的账户对象
private account acc;
public drawthread2(account acc,String name){
super(name);
this.acc=acc;
}
public void run(){
//取钱
acc.drawmoney(10000);
}
}
class account2{
private String cartId;
private double money;//账户余额
//final修饰后:锁对象是唯一的和不可替换的
//Lock是接口不能直接实例化,我们需要采用它的实现类ReentrantLock来构建Lock锁对象
private final Lock lock=new ReentrantLock();
public account2() {
}
public account2(String cartId, double money) {
this.cartId = cartId;
this.money = money;
}
public String getCartId() {
return cartId;
}
public void setCartId(String cartId) {
this.cartId = cartId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawmoney(double money) {
//先获取是谁来取钱,线程名即是人名
String name=Thread.currentThread().getName();
lock.lock();//上锁
//判断账户是否够钱
try {
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
//防止代码出现bug而不能解锁
} finally {
lock.unlock();//解锁
}
}
}
来源:https://blog.csdn.net/qq_62731133/article/details/124420818
标签:Java,线程,安全,同步
0
投稿
猜你喜欢
SpringBoot实现简单文件上传功能
2021-07-22 21:52:49
解决Spring boot整合mybatis,xml资源文件放置及路径配置问题
2021-07-07 12:27:20
springboot html调用js无效400问题及解决
2023-06-24 02:11:54
Android仿iOS侧滑退出当前界面功能
2022-07-01 10:08:37
深入理解Java并发编程之LinkedBlockingQueue队列
2022-10-02 06:35:22
Android开发应用中Broadcast Receiver组件详解
2023-04-25 09:35:35
浅谈java常量池
2023-11-18 09:43:50
C#中WPF内存回收与释放LierdaCracker的实现
2022-11-13 00:15:44
java中Integer包装类装箱的一个细节详解
2023-09-24 18:35:32
使用Springboot自定义注解,支持SPEL表达式
2023-11-20 01:18:58
java实现把对象数组通过excel方式导出的功能
2022-07-01 13:20:09
Java正确使用访问修饰符的姿势
2021-10-11 09:52:50
Jetpack Compose实现列表和动画效果详解
2022-07-16 21:14:44
java启动jar包修改JVM默认内存问题
2022-03-17 21:50:09
C#实现Stripe支付的方法实践
2023-01-22 01:46:47
使用Springboot自定义转换器实现参数去空格功能
2021-11-12 03:36:29
Spring Boot提高开发效率必备工具lombok使用
2022-05-21 08:22:38
C#实现自定义定时组件的方法
2023-06-04 12:36:26
Java中的双重检查(Double-Check)详解
2023-02-23 21:05:23
Android中使用Toast.cancel()方法优化toast内容显示的解决方法
2021-12-14 05:17:03