线程同步

线程同步

并发:同一个对象被多个线程同时操作

  • 一个线程持有锁会导致其他所有需要此锁的线程挂起

  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题

  • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

 

同步方法

  • 方法里面需要修改的内容才需要锁

    package com.Spp.syn;
    ?
    // 不安全的买票
    public class UnSafeBuyTicket {
       public static void main(String[] args) {
           BuyTicket station = new BuyTicket();
           new Thread(station,"你").start();
           new Thread(station,"我").start();
           new Thread(station,"他").start();
      }
    }
    ?
    class BuyTicket implements Runnable{// 涉及多个线程操作同一个对象
       private int ticketNumbers = 10;
       boolean flag = true;// 外部停止方式
       @Override
       public void run() {
           while (flag){
               try {
                   Buy();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      }
       //synchronized 同步方法,锁的是this
       private synchronized void Buy() throws InterruptedException {
           // 判断是否有票
           if(ticketNumbers <= 0){
               flag = false;
               return;
          }
           // 模拟延时
           Thread.sleep(100);
           // 买票
           System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNumbers--+"张票");
      }
    }

     

同步块:

  • synchronized(Obj){}

  • Obj称之为同步监视器

    • Obj可以是任何对象,推荐使用共享资源作为同步监视器

    • 同步方法中无需指定同步监视器,同步方法的同步监视器及时this,就是这个对象本身,或者是class

  • 同步监视器的执行过程

    1. 第一个线程访问,锁定同步监视器,执行其中代码

    2. 第二个线程访问,发现同步监视器被锁定,无法访问

    3. 第一个线程访问完毕,解锁同步监视器

    4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

package com.Spp.syn;
?
public class UnSafeBank {
   public static void main(String[] args) {
       Account account = new Account(3600,"备用金");
       Drawing dra1 = new Drawing(account,280,"dra1");
       Drawing dra2 = new Drawing(account,100,"dra2");
       dra1.start();
       dra2.start();
  }
}
?
// 账户
class Account {
   int money;
   String name;
?
   public Account(int money, String name) {
       this.money = money;
       this.name = name;
  }
}
?
//银行:模拟取款
class Drawing extends Thread {// 不涉及多个线程操作同一个对象
   Account account;
   // 要取的钱
   int takeMoney;
   // 现在手里有多少钱
   int nowMoney;
   public Drawing( Account account,int takeMoney,String name){
       super(name);
       this.account = account;
       this.takeMoney = takeMoney;
  }
?
   @Override
   public void run() {// 取钱
       //锁的对象就是变化的量,需要增删改查的对象
       synchronized (account){
           // 判断钱够不够
           if(account.money-takeMoney<0){
               System.out.println(Thread.currentThread().getName()+"钱不够,取不了这么多");
               return;
          }
           try {
               sleep(100);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
           account.money = account.money-takeMoney;
           nowMoney = nowMoney+takeMoney;
           System.out.println(account.name+"余额为"+account.money);
           //Thread.currentThread().getName() = this.getName()
           System.out.println(this.getName()+"手里的钱为"+nowMoney);
      }
      }
?
}

 

死锁

执行卡死

package com.kuang.ThreadLesson;
?
public class DeadLock {
   public static void main(String[] args) {
       MakeUp g1 = new MakeUp(0,"11");
       MakeUp g2 = new MakeUp(1,"22");
       g1.start();
       g2.start();
  }
}
//口红
class LipStick{
?
}
//镜子
class Mirror{
?
}
?
class MakeUp extends Thread{
   // 需要的资源只有一份,用static来保证只有一份
   static LipStick lipStick = new LipStick();
   static Mirror mirror = new Mirror();
   int choice;// 选择
   String name;// 使用的人
   MakeUp(int choice,String name){
       this.choice = choice;
       this.name = name;
  }
   @Override
   public void run() {
       try {
           makeup();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
  }
   private void makeup() throws InterruptedException {
       if(choice == 0){
           synchronized (lipStick){
               System.out.println(this.name+"获得口红的锁");
               Thread.sleep(100);
               synchronized (mirror){
                   System.out.println(this.name+"获得镜子的锁");
              }
          }
?
      }else{
           synchronized (mirror){
               System.out.println(this.name+"获得镜子的锁");
               Thread.sleep(200);
               synchronized (lipStick){
                   System.out.println(this.name+"获得口红的锁");
              }
          }
?
      }
  }
}

顺利执行

 private void makeup() throws InterruptedException {
       if(choice == 0){
           synchronized (lipStick){
               System.out.println(this.name+"获得口红的锁");
               Thread.sleep(100);
          }
           synchronized (mirror){
               System.out.println(this.name+"获得镜子的锁");
          }
      }else{
           synchronized (mirror){
               System.out.println(this.name+"获得镜子的锁");
               Thread.sleep(200);
          }
           synchronized (lipStick){
               System.out.println(this.name+"获得口红的锁");
          }
      }
  }

Lock(锁)

ReentrantLock:可重入锁

ReentrantLock类实现Lock锁

package com.kuang.ThreadLesson;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
   public static void main(String[] args) {
       BuyTicket buyTicket = new BuyTicket();
       new Thread(buyTicket).start();
       new Thread(buyTicket).start();
       new Thread(buyTicket).start();
  }
}
?
class BuyTicket implements Runnable{
   int ticketNumbers = 10;
   //定义Lock锁
   private final ReentrantLock lock = new ReentrantLock();
?
   @Override
   public void run() {
       while (true) {
           try {
               lock.lock();//加锁
               if (ticketNumbers > 0) {
                   try {
                       Thread.sleep(1000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                   System.out.println(ticketNumbers--);
              }
          } finally {
               lock.unlock();//解锁
          }
      }
  }
}
synchronized 与Lock的对比
  • Lock是显示锁(手动开启和关闭),synchronized是隐式锁,出了作用域自动释放

  • Lock只有代码块锁,synchronized有代码块锁和方法锁

  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

  • 优先使用顺序:Lock > 同步代码块 > 同步方法

线程同步

上一篇:Spring中AOP的实现


下一篇:Delphi 系统[16]关键字和保留字 cdecl、pascal、stdcall、safecall、register、varargs - 调用约定