ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

线程同步机制

2022-07-03 11:05:40  阅读:111  来源: 互联网

标签:acount 同步 Thread 线程 new 机制 public


线程同步

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

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

  • 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问是的正确性,在访问时加入了 锁机制 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());
    }
 }
 

标签:acount,同步,Thread,线程,new,机制,public
来源: https://www.cnblogs.com/Jason-fan/p/16439385.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有