JAVA深化篇_32—— 线程使用之线程同步synchronized语法结构【附有详细说明及代码】

线程同步

什么是线程同步

同步问题的提出

现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题。 比如:教室里,只有一台电脑,多个人都想使用。天然的解决办法就是,在电脑旁边,大家排队。前一人使用完后,后一人再使用。

线程同步的概念

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

线程冲突案例演示

我们以银行取款经典案例来演示线程冲突现象。

银行取钱的基本流程基本上可以分为如下几个步骤。

(1)用户输入账户、密码,系统判断用户的账户、密码是否匹配。

(2)用户输入取款金额

(3)系统判断账户余额是否大于或等于取款金额

(4)如果余额大于或等于取款金额,则取钱成功;如果余额小于取款金额,则取钱失败。

/**
 * 账户类
 */
class Account{
  //账号
  private String accountNo;
  //账户的余额
  private double balance;


  public Account() {
   }


  public Account(String accountNo, double balance) {
    this.accountNo = accountNo;
    this.balance = balance;
   }


  public String getAccountNo() {
    return accountNo;
   }


  public void setAccountNo(String accountNo) {
    this.accountNo = accountNo;
   }


  public double getBalance() {
    return balance;
   }


  public void setBalance(double balance) {
    this.balance = balance;
   }
}
/**
 * 取款线程
 */
class DrawThread implements Runnable{
  //账户对象
  private Account account;
  //取款金额
  private double drawMoney;
  public DrawThread(Account account,double drawMoney){
    this.account = account;
    this.drawMoney = drawMoney;
   }


  /**
   * 取款线程
   */
  @Override
  public void run() {
    //判断当前账户余额是否大于或等于取款金额
    if(this.account.getBalance() >= this.drawMoney){
      System.out.println(Thread.currentThread().getName()+" 取钱成功!吐出钞票:"+this.drawMoney);
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
      //更新账户余额
      this.account.setBalance(this.account.getBalance()- this.drawMoney);
      System.out.println("\t 余额为:"+this.account.getBalance());
     }else{
      System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");
     }
   }
}


public class TestDrawMoneyThread {
  public static void main(String[] args) {
    Account account = new Account("1234",1000);
    new Thread(new DrawThread(account,800),"老公").start();
    new Thread(new DrawThread(account,800),"老婆").start();
   }
}

实现线程同步

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。这套机制就是synchronized关键字。

synchronized语法结构:

synchronized(锁对象){   
	同步代码  
}

synchronized关键字使用时需要考虑的问题:

  • 需要对那部分的代码在执行时具有线程互斥的能力(线程互斥:并行变串行)。
  • 需要对哪些线程中的代码具有互斥能力(通过synchronized锁对象来决定)。

它包括两种用法:

synchronized 方法和 synchronized 块。

  • synchronized 方法

    通过在方法声明中加入 synchronized关键字来声明,语法如下:

public synchronized void accessVal(int newVal);

  • synchronized 在方法声明时使用:放在访问控制符(public)之前或之后。这时同一个对象下synchronized方法在多线程中执行时,该方法是同步的,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入。

  • synchronized块

    synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

    Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。

修改线程冲突案例演示

/**
 * 账户类
 */
class Account{
  //账号
  private String accountNO;
  //账户余额
  private double balance;
  public Account() {
   }
  public Account(String accountNO, double balance) {
    this.accountNO = accountNO;
    this.balance = balance;
   }
  public String getAccountNO() {
    return accountNO;
   }
  public void setAccountNO(String accountNO) {
    this.accountNO = accountNO;
   }
  public double getBalance() {
    return balance;
   }
  public void setBalance(double balance) {
    this.balance = balance;
   }
}
/**
 * 取款线程
 */
class DrawThread implements Runnable{
  //账户对象
  private Account account;
  //取款金额
  private double drawMoney;
  public DrawThread(){
   }
  public DrawThread(Account account,double drawMoney){
    this.account = account;
    this.drawMoney = drawMoney;
   }
  /**
   * 取款线程体
   */
  @Override
  public void run() {
    synchronized (this.account){
      //判断当前账户余额是否大于或等于取款金额
      if(this.account.getBalance() >= this.drawMoney){
        System.out.println(Thread.currentThread().getName()+" 取钱成功!突出钞票"+this.drawMoney);
        try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        //更新账户余额
        this.account.setBalance(this.account.getBalance() - this.drawMoney);
        System.out.println("\t 余额为:"+this.account.getBalance());
       }else{
        System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");
       }
     }
   }
}
public class TestDrawMoneyThread {
  public static void main(String[] args) {
    Account account = new Account("1234",1000);
    new Thread(new DrawThread(account,800),"老公").start();
    new Thread(new DrawThread(account,800),"老婆").start();
   }
}

线程同步的使用

使用this作为线程对象锁

语法结构:

synchronized(this){ 
    //同步代码 
   }

public synchronized void accessVal(int newVal){
	//同步代码
	}

/**
 * 定义程序员类
 */
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  /**
   * 打开电脑
   */
  synchronized public void computer(){
      try {
        System.out.println(this.name + " 接通电源");
        Thread.sleep(500);
        System.out.println(this.name + " 按开机按键");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动中");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动成功");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
  /**
   * 编码
   */
  synchronized public void coding(){
      try {
        System.out.println(this.name + " 双击Idea");
        Thread.sleep(500);
        System.out.println(this.name + " Idea启动完毕");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的写代码");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}


/**
 * 打开电脑的工作线程
 */
class Working1 extends Thread{
  private Programmer p;
  public Working1(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.computer();
   }
}


/**
 * 编写代码的工作线程
 */
class Working2 extends Thread{
  private Programmer p;
  public Working2(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.coding();
   }
}
public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p = new Programmer("张三");
    new Working1(p).start();
    new Working2(p).start();
   }
}

使用字符串作为线程对象锁

语法结构:

synchronized(“字符串”){ 
    //同步代码 
   }

/**
 * 定义程序员类
 */
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  /**
   * 打开电脑
   */
  synchronized public void computer(){
      try {
        System.out.println(this.name + " 接通电源");
        Thread.sleep(500);
        System.out.println(this.name + " 按开机按键");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动中");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动成功");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
  /**
   * 编码
   */
  synchronized public void coding(){
      try {
        System.out.println(this.name + " 双击Idea");
        Thread.sleep(500);
        System.out.println(this.name + " Idea启动完毕");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的写代码");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
  /**
   * 去卫生间
   */
  public void wc(){
    synchronized ("suibian") {
      try {
        System.out.println(this.name + " 打开卫生间门");
        Thread.sleep(500);
        System.out.println(this.name + " 开始排泄");
        Thread.sleep(500);
        System.out.println(this.name + " 冲水");
        Thread.sleep(500);
        System.out.println(this.name + " 离开卫生间");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}


/**
 * 打开电脑的工作线程
 */
class Working1 extends Thread{
  private Programmer p;
  public Working1(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.computer();
   }
}


/**
 * 编写代码的工作线程
 */
class Working2 extends Thread{
  private Programmer p;
  public Working2(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.coding();
   }
}


/**
 * 去卫生间的线程
 */
class WC extends Thread{
  private Programmer p;
  public WC(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.wc();
   }
}
public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p = new Programmer("张三");
    Programmer p1 = new Programmer("李四");
    Programmer p2 = new Programmer("王五");
    new WC(p).start();
    new WC(p1).start();
    new WC(p2).start();
   }
}

用Class作为线程对象锁

语法结构:

synchronized(XX.class){ 
    //同步代码 
   }

synchronized public static void accessVal()

/**
 * 定义销售员工类
 */
class Sale{
  private String name;
  public Sale(String name){
    this.name = name;
   }
  /**
   * 领取奖金
   */
  synchronized public static void money(){
      try {
        System.out.println(Thread.currentThread().getName() + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 拿钱");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  /**
   * 打开电脑
   */
  synchronized public void computer(){
      try {
        System.out.println(this.name + " 接通电源");
        Thread.sleep(500);
        System.out.println(this.name + " 按开机按键");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动中");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动成功");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
  /**
   * 编码
   */
  synchronized public void coding(){
      try {
        System.out.println(this.name + " 双击Idea");
        Thread.sleep(500);
        System.out.println(this.name + " Idea启动完毕");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的写代码");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
  /**
   * 去卫生间
   */
  public void wc(){
    synchronized ("suibian") {
      try {
        System.out.println(this.name + " 打开卫生间门");
        Thread.sleep(500);
        System.out.println(this.name + " 开始排泄");
        Thread.sleep(500);
        System.out.println(this.name + " 冲水");
        Thread.sleep(500);
        System.out.println(this.name + " 离开卫生间");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
  /**
   * 领取奖金
   */
  public void money(){
    synchronized (Programmer.class) {
      try {
        System.out.println(this.name + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(this.name + " 拿钱");
        Thread.sleep(500);
        System.out.println(this.name + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}


/**
 * 打开电脑的工作线程
 */
class Working1 extends Thread{
  private Programmer p;
  public Working1(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.computer();
   }
}


/**
 * 编写代码的工作线程
 */
class Working2 extends Thread{
  private Programmer p;
  public Working2(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.coding();
   }
}


/**
 * 去卫生间的线程
 */
class WC extends Thread{
  private Programmer p;
  public WC(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.wc();
   }
}


/**
 * 程序员领取奖金
 */
class ProgrammerMoney extends Thread{
  private Programmer p;
  public ProgrammerMoney(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}


/**
 * 销售部门领取奖金
 */
class SaleMoney extends Thread{
  private Sale p;
  public SaleMoneyThread(Sale p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}


public class TestSyncThread {
  public static void main(String[] args) {
    /*  Programmer p = new Programmer("张三");
    Programmer p1 = new Programmer("李四");
    new ProgrammerMoney(p).start();
    new ProgrammerMoney(p1).start();*/
    
    Sale s = new Sale("张晓丽");
    Sale s1 = new Sale("王晓红");
    new SaleMoney(s).start();
    new SaleMoney(s1).start();
   }
}

使用自定义对象作为线程对象锁

语法结构:

synchronized(自定义对象){ 
    //同步代码 
}

/**
 * 定义销售员工类
 */
class Sale{
  private String name;
  public Sale(String name){
    this.name = name;
   }
  /**
   * 领取奖金
   */
  synchronized public static void money(){
      try {
        System.out.println(Thread.currentThread().getName() + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 拿钱");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  /**
   * 打开电脑
   */
  synchronized public void computer(){
      try {
        System.out.println(this.name + " 接通电源");
        Thread.sleep(500);
        System.out.println(this.name + " 按开机按键");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动中");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动成功");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
  /**
   * 编码
   */
  synchronized public void coding(){
      try {
        System.out.println(this.name + " 双击Idea");
        Thread.sleep(500);
        System.out.println(this.name + " Idea启动完毕");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的写代码");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
  /**
   * 去卫生间
   */
  public void wc(){
    synchronized ("suibian") {
      try {
        System.out.println(this.name + " 打开卫生间门");
        Thread.sleep(500);
        System.out.println(this.name + " 开始排泄");
        Thread.sleep(500);
        System.out.println(this.name + " 冲水");
        Thread.sleep(500);
        System.out.println(this.name + " 离开卫生间");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
  /**
   * 领取奖金
   */
  public void money(){
    synchronized (Programmer.class) {
      try {
        System.out.println(this.name + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(this.name + " 拿钱");
        Thread.sleep(500);
        System.out.println(this.name + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}
class Manager{
  private String name;
  public Manager(String name){
    this.name = name;
   }
  public String getName(){
    return this.name;
   }
  /**
   * 敬酒
   */
  public void cheers(String mName,String eName){
      try {
        System.out.println(mName + " 来到 " + eName + " 面前");
        Thread.sleep(500);
        System.out.println(eName + " 拿起酒杯");
        Thread.sleep(500);
        System.out.println(mName + " 和 " + eName + " 干杯");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
}
/**
 * 打开电脑的工作线程
 */
class Working1 extends Thread{
  private Programmer p;
  public Working1(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.computer();
   }
}


/**
 * 编写代码的工作线程
 */
class Working2 extends Thread{
  private Programmer p;
  public Working2(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.coding();
   }
}


/**
 * 去卫生间的线程
 */
class WC extends Thread{
  private Programmer p;
  public WC(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.wc();
   }
}


/**
 * 程序员领取奖金
 */
class ProgrammerMoney extends Thread{
  private Programmer p;
  public ProgrammerMoney(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}


/**
 * 销售部门领取奖金
 */
class SaleMoneyThread extends Thread{
  private Sale p;
  public SaleMoneyThread(Sale p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}


/**
 * 敬酒线程类
 */
class CheersThread extends Thread{
  private Manager manager;
  private String name;
  public CheersThread(String name,Manager manager){
    this.name = name;
    this.manager = manager;
   }
  @Override
  public void run() {
    synchronized (this.manager) {
      this.manager.cheers(this.manager.getName(), name);
     }
   }
}


public class TestSyncThread {
  public static void main(String[] args) {
    Manager manager = new Manager("张三丰");
    new CheersThread("张三",manager).start();
    new CheersThread("李四",manager).start();


   }
}

死锁及解决方案

死锁的概念

“死锁”指的是:

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

某一个同步块需要同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。比如,“化妆线程”需要同时拥有“镜子对象”、“口红对象”才能运行同步块。那么,实际运行时,“小丫的化妆线程”拥有了“镜子对象”,“大丫的化妆线程”拥有了“口红对象”,都在互相等待对方释放资源,才能化妆。这样,两个线程就形成了互相等待,无法继续运行的“死锁状态”。

死锁案例演示

/**
 * 口红类
 */
class Lipstick{


}


/**
 * 镜子类
 */
class Mirror{


}


/**
 * 化妆线程类
 */
class Makeup extends Thread{
  private int flag; //flag=0:拿着口红。flag!=0:拿着镜子
  private String girlName;
  static Lipstick lipstick = new Lipstick();
  static Mirror mirror = new Mirror();


  public Makeup(int flag,String girlName){
    this.flag = flag;
    this.girlName = girlName;
   }


  @Override
  public void run() {
    this.doMakeup();
   }
  /**
   * 开始化妆
   */
  public void doMakeup(){
    if(flag == 0){
      synchronized (lipstick){
        System.out.println(this.girlName+" 拿着口红");
        try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        synchronized (mirror){
          System.out.println(this.girlName+" 拿着镜子");
         }
       }
     }else{
      synchronized (mirror){
        System.out.println(this.girlName+" 拿着镜子");
        try {
          Thread.sleep(2000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        synchronized (lipstick){
          System.out.println(this.girlName+" 拿着口红");
         }
       }
     }
   }
}


public class DeadLockThread {
  public static void main(String[] args) {
    new Makeup(0,"大丫").start();
    new Makeup(1,"小丫").start();
   }
}

死锁问题的解决

死锁是由于 “同步块需要同时持有多个对象锁造成”的,要解决这个问题,思路很简单,就是:同一个代码块,不要同时持有两个对象锁。

/**
 * 口红类
 */
class Lipstick{


}


/**
 * 镜子类
 */
class Mirror{


}


/**
 * 化妆线程类
 */
class Makeup extends Thread{
  private int flag; //flag=0:拿着口红。flag!=0:拿着镜子
  private String girlName;
  static Lipstick lipstick = new Lipstick();
  static Mirror mirror = new Mirror();


  public void setFlag(int flag) {
    this.flag = flag;
   }


  public void setGirlName(String girlName) {
    this.girlName = girlName;
   }


  @Override
  public void run() {
    this.doMakeup();
   }
  /**
   * 开始化妆
   */
  public void doMakeup(){
    if(flag == 0){
      synchronized (lipstick){
        System.out.println(this.girlName+" 拿着口红");
        try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
       }
      synchronized (mirror){
        System.out.println(this.girlName+" 拿着镜子");
       }
     }else{
      synchronized (mirror){
        System.out.println(this.girlName+" 拿着镜子");
        try {
          Thread.sleep(2000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
       }
      synchronized (lipstick){
        System.out.println(this.girlName+" 拿着口红");
       }
     }
   }
}


public class DeadLockThread {
  public static void main(String[] args) {
    Makeup makeup = new Makeup();
    makeup.setFlag(0);
    makeup.setGirlName("大丫");
    Makeup makeup1 = new Makeup();
    makeup1.setFlag(1);
    makeup1.setGirlName("小丫");
    makeup.start();
    makeup1.start();
   }
}

你可能感兴趣的:(JAVA,基础深化和提高,java,开发语言)