Java 多线程(Java.Thread)----线程同步(并发问题处理)

线程同步

并发

  • 并发:同一个对象被多个线程同时操作:多人同时抢1张票、多人同时在一个账号里取钱
//并发问题举例1:
//不安全的买票:会出现负数、0、不同的人抢到同一张表
package www.bh.c.threadtest;

public class BuyTickets implements Runnable{
    
    
    public static void main(String[] args) {
    
    
        BuyTickets buyTickets = new BuyTickets();
        new Thread(buyTickets,"你").start();
        new Thread(buyTickets,"我").start();
        new Thread(buyTickets,"他").start();

    }

    private int tickets=10;
    boolean flag=true;
    @Override
    public void run() {
    
    
        while (flag){
    
    
            try {
    
    
                buy();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
    }
    public void buy(){
    
    
        //判断是否有票
        if (tickets<=0){
    
    
            flag=false;
            return;
        }
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"拿到了第"+tickets--+"张票");
    }
}
//并发问题举例2:
//不安全的取钱:账户余额会出现负数
package www.bh.c.threadtest;

public class GetMoney {
    
    
    public static void main(String[] args) {
    
    
        Account account = new Account(100,"学费");
        Drawing you = new Drawing(account, 50, "you");
        Drawing yousister = new Drawing(account, 100, "sister");
        yousister.start();
        you.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 drawingMoney;
    //取到手的钱
    int nowMoney;
    public Drawing(Account account,int drawingMoney,String name){
    
    
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;
    }
    @Override
    public void run() {
    
    
        //判断余额是否足够
        if (account.money-drawingMoney<0){
    
    
            System.out.println(Thread.currentThread().getName()+"账户余额不足");
            return;
        }
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //卡内余额=余额-取得额度
        account.money=account.money-drawingMoney;
        //手里的钱
        nowMoney=nowMoney+drawingMoney;
        System.out.println(account.name+"余额为"+account.money);
        //Thread.currentThread().getName()=this.getName()
        System.out.println(this.getName()+"手里的总额"+nowMoney);
    }
}
//并发问题举例3:
//不安全的集合:添加不全
package www.bh.c.threadtest;
import java.util.ArrayList;
import java.util.List;

public class ListTest {
    
    
    public static void main(String[] args) {
    
    
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
    
    
            new Thread(()-> {
    
    list.add(Thread.currentThread().getName());}).start();
        }
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println(list.size());//<999
    }
}
  • 处理多线程问题时,多个线程访问一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步;线程同步其实是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用

  • 形成条件:队列+锁

  • 锁机制:synchronized:当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,锁机制存在以下问题:

    • 一个线程持有锁会导致其他所有此锁的线程挂起
    • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
  • 同步方法:synchronized关键字(默认锁的是this)

    • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

    • 缺陷:将一个大的方法申明为synchronized将会影响效率,所以方法里面需要修改的内容才需要锁,锁的太多,浪费资源

    • 用法一:synchronized方法:public synchronized void method(int args){}

    • 用法二:synchronized块:synchronized(Obj){}

      • Obj称之为同步监视器,推荐使用共享资源作为同步监视器
      • 同步监视器的执行过程:
        • 1.第一个线程访问,锁定同步监视器,执行其中代码
        • 2.第二个线程访问,发现同步监视器被锁定,无法访问
        • 3.第一个线程访问完毕,解锁同步监视器
        • 4.第二个线程访问,发现同步监视器没有锁,锁定同步监视器,执行其中代码
      //并发问题举例1:
      //不安全的买票:会出现负数、0、不同的人抢到同一张表
      //解决方案:synchronized 同步方法
      package www.bh.c.threadtest;
      
      public class BuyTickets implements Runnable{
              
              
          public static void main(String[] args) {
              
              
              BuyTickets buyTickets = new BuyTickets();
              new Thread(buyTickets,"你").start();
              new Thread(buyTickets,"我").start();
              new Thread(buyTickets,"他").start();
      
          }
      
          private int tickets=10;
          boolean flag=true;
          @Override
          public void run() {
              
              
              while (flag){
              
              
                  try {
              
              
                      buy();
                  } catch (Exception e) {
              
              
                      e.printStackTrace();
                  }
              }
          }
          public synchronized void buy(){
              
                //synchronized 同步方法
              //判断是否有票
              if (tickets<=0){
              
              
                  flag=false;
                  return;
              }
              try {
              
              
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
              
              
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName()+"拿到了第"+tickets--+"张票");
          }
      }
      
      //并发问题举例2:
      //不安全的取钱:账户余额会出现负数
      //解决方案:synchronized 同步块
      package www.bh.c.threadtest;
      
      public class GetMoney {
              
              
          public static void main(String[] args) {
              
              
              Account account = new Account(100,"学费");
              Drawing you = new Drawing(account, 50, "you");
              Drawing yousister = new Drawing(account, 100, "sister");
              yousister.start();
              you.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 drawingMoney;
          //取到手的钱
          int nowMoney;
          public Drawing(Account account,int drawingMoney,String name){
              
              
              super(name);
              this.account=account;
              this.drawingMoney=drawingMoney;
          }
          @Override
          public void run() {
              
              
              //同步块,锁的对象是变化的量,需要增删改的量
              synchronized (account){
              
              
                  //判断余额是否足够
                  if (account.money-drawingMoney<0){
              
              
                      System.out.println(Thread.currentThread().getName()+"账户余额不足");
                      return;
                  }
                  try {
              
              
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
              
              
                      e.printStackTrace();
                  }
                  //卡内余额=余额-取得额度
                  account.money=account.money-drawingMoney;
                  //手里的钱
                  nowMoney=nowMoney+drawingMoney;
                  System.out.println(account.name+"余额为"+account.money);
                  //Thread.currentThread().getName()=this.getName()
                  System.out.println(this.getName()+"手里的总额"+nowMoney);
              }
          }
      }
      
      //并发问题举例3:
      //不安全的集合:添加不全
      //解决方案1:synchronized 同步块
      package www.bh.c.threadtest;
      import java.util.ArrayList;
      import java.util.List;
      
      public class ListTest {
              
              
          public static void main(String[] args) {
              
              
              List<String> list = new ArrayList<>();
              for (int i = 0; i < 1000; i++) {
              
              
                  new Thread(()->  {
              
              synchronized (list){
              
              list.add(Thread.currentThread().getName());}
                  }).start();
              }
              try {
              
              
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
              
              
                  e.printStackTrace();
              }
              System.out.println(list.size());
          }
      }
      
      //解决方案2:JUC安全类型的集合
      package www.bh.c.threadtest;
      import java.util.concurrent.CopyOnWriteArrayList;
      
      public class JucListTest {
              
              
          public static void main(String[] args) {
              
              
              CopyOnWriteArrayList copy=new CopyOnWriteArrayList<String>();
              for (int i = 0; i < 1000; i++) {
              
              
                  new Thread(()->{
              
              copy.add(Thread.currentThread().getName());
                  }).start();
              }
              try {
              
              
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
              
              
                  e.printStackTrace();
              }
              System.out.println(copy.size());//1000
          }
      }
      

猜你喜欢

转载自blog.csdn.net/insist_to_learn/article/details/111121592
今日推荐