线程同步机制

线程同步

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

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

  • 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问是的正确性,在访问时加入了 锁机制 synchronized ,当一个线程获得对象的排它锁,独占资源,其它线程必须瞪大,使用后释放锁即可。存在以下问题:

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

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

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

方法里需要修改的内容才需要锁,锁太多会浪费资源

同步方法

  • 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是 sychronized 关键字,他包括两种用法 sychronized 方法和 sychronized 块。

    同步方法:public sychronized void method(int args){}

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

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

 

同步块

  • 同步块: sychronized(Obj){}

  • Obj 称之为 同步监视器

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

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

  • 同步监视器的执行过程

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

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

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

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

 

附录

 //不安全的买票
 //线程不安全,有重复的票
 //加上synchronized,锁住代码块(原理:队列+锁)
 
 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 ticketNums = 10;
     private boolean flag = true;//外部停止方式
 
 
     @Override
     public void run() {
         //买票
         while(flag){
             try {
                 buy();
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
 
    }
 
 /*   private void buy() throws InterruptedException {
         //判断是否有票
         if(ticketNums <= 0){
             flag = false;
             return;
         }
 
         //模拟延时
         Thread.sleep(100);
 
         System.out.println(Thread.currentThread().getName()+"买到了票"+ticketNums--);
 
     }*/
 
     //synchronized 同步方法,锁的是this
     private synchronized void buy() throws InterruptedException {
         //判断是否有票
         if(ticketNums <= 0){
             flag = false;
             return;
        }
 
         //模拟延时
         Thread.sleep(100);
 
         System.out.println(Thread.currentThread().getName()+"买到了票"+ticketNums--);
 
    }
 
 }

 

 //不安全的取钱
 //两个人去银行取钱,账户
 public class UnsafeBank {
     public static void main(String[] args) {
         //账户
         Acount acount = new Acount(100,"结婚基金");
 
         Drawing you = new Drawing(acount,50,"你");
         Drawing yourWife = new Drawing(acount,100,"你妻子");
 
         you.start();
         yourWife.start();
    }
 }
 
 
 //账户
 class Acount{
     int money;
     String name;
 
     public Acount(int money, String name) {
         this.money = money;
         this.name = name;
    }
 }
 
 
 //银行,模拟取款
 class Drawing extends Thread{
 
     Acount acount;//账户
     int drawingMoney;//取了多少钱
     int nowMoney;//现在手里有多少钱
 
     public Drawing(Acount acount, int drawingMoney, String name) {
         super(name);
         this.acount = acount;
         this.drawingMoney = drawingMoney;
 
    }
 
     //取钱
     //synchronized 默认锁是this,在run加锁,锁的是this,即Drawing,此处需要锁的是acount
     @Override
 //   public synchronized void run() {
     public void run() {
 
         //锁的对象就是变化的量,需要增删改的对象
         synchronized(acount){
             //判断有没有钱
             if(acount.money-drawingMoney<0){
                 System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                 return;
            }
             //模拟延时,sleep()可以放大问题的发生性
             try {
                 Thread.sleep(100);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
             //卡内余额
             acount.money = acount.money-drawingMoney;
             //你手里的钱
             nowMoney = nowMoney+drawingMoney;
 
             System.out.println(acount.name+"余额为:"+acount.money);
 
             System.out.println(this.getName()+"手里的钱:"+nowMoney);
        }
    }
 }

 

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 (list){
                     list.add(Thread.currentThread().getName());
                }
            }).start();
        }
 
         try {
             Thread.sleep(3000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
 
         System.out.println(list.size());
         //size 不到10000,因为在线程执行过程中,出现了多个线程添加在同一个位置,所以它不安全
    }
 }

 

 //测试JUC安全类型的集合
 public class TestJUC {
     public static void main(String[] args) {
         CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
         for (int i = 0; i < 10000; i++) {
             new Thread(()->{
                 list.add(Thread.currentThread().getName());
            }).start();
        }
 
         try {
             Thread.sleep(3000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
 
         System.out.println(list.size());
    }
 }
 
posted @   小gun  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示