多线程---同步方法及同步块(解决线程不安全)

同步方法

 

 

 


 

 

 

注意:锁的量是变化的量,需要增删改的对象

同步方法与同步代码块的区别:

同步方法:锁的粒度粗,执行效率低;

同步代码块:锁的粒度细,执行效率高;

 

三大不安全案例解决方案:

案例1(火车站买票)

package com.mokuiran.thread.synchronizedtest;

//不安全买票
//线程不安全,将会输出负数
public class UnsafeBuyTicket{

   public static void main(String[] args) {
       BuyTicket buyTicket = new BuyTicket();

       Thread thread1 = new Thread(buyTicket,"小明");
       Thread thread2 = new Thread(buyTicket,"大明");
       Thread thread3 = new Thread(buyTicket,"无明");
       thread1.start();
       thread2.start();
       thread3.start();

  }
}
class BuyTicket implements Runnable{
   private int ticketNum = 10;//总票数
   boolean flag = true;//外部停止方式
   @Override
   public void run() {
       //买票
       while (flag){
           buy();
      }
  }
//----------解决方法--------------,在buy方法这加synchronized关键字
   private synchronized void  buy(){
       //判断是否有票
       if (ticketNum<=0){
           flag = false;
           return;
      }
       //模拟延时
       try {
           Thread.sleep(100);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       //买票
       System.out.println(Thread.currentThread().getName()+"拿到"+ticketNum--);
  }

}

案例2(银行取钱)

package com.mokuiran.thread.synchronizedtest;

//不安全的取钱
//两个人去银行取钱

public class UnsafeBank {
   public static void main(String[] args) {
       //账户
       Account account = new Account(1000,"结婚基金");

       Drawing you = new Drawing(account,50,"你");
       Drawing grilFriend = new Drawing(account,100,"妻子");

       you.start();
       grilFriend.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代码块
       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(List集合)

package com.mokuiran.thread.synchronizedtest;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合

public class UnsafeList {
   public static void main(String[] args){
       List<String> list = new ArrayList<>();
       for (int i = 0; i < 10000; i++) {
           new Thread(() -> {
               
        //---------解决方法:加入synchronized代码块      
               synchronized (list){
                   list.add(Thread.currentThread().getName());
              }
          }).start();
      }
       try {
           Thread.sleep(3000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(list.size());
       /*
       打印的大小不到一万,因为两个线程会抢占同一个资源,发生元素的覆盖,导致
       元素变少
        */
  }
}

 

扩充

package com.mokuiran.thread.synchronizedtest;

import java.util.concurrent.CopyOnWriteArrayList;

//安全
//测试JUC安全类型的集合
public class TestJUC {
   public static void main(String[] args) {
       CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
       for (int i = 0; i < 10000; i++) {
           new Thread(() -> {
               list.add(Thread.currentThread().getName());
          }).start();
      }
       try {
           Thread.sleep(1000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(list.size());//输出10000
  }
}
 
posted @   默夔然  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示