> 文档中心 > 线程安全问题解决之线程同步

线程安全问题解决之线程同步


第一种方法:同步代码块:

作用:把出现线程安全的核心代码上锁

原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行

对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可

缺点:会干扰其他无关线程的执行

所以,这种只是理论上的,了解即可,现实中并不会这样用

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();//解锁 }    }}

化妆学校