• 多线程15:线程同步


    同步方法: 

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

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

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

    缺陷:若将一个大的方法申明为sychronized将会影响效率
    同步方法弊端:
    只读代码,不需要同步每个人都可以读,读的时候是不会有错的
    修改代码的时候才需要同步。所以还有个 sychronized 块,
    方法里面需要修改的内容的才需要锁,锁的太多,浪费资源。
    所以说同步方法有时候也不是那么高效。
    同步块:
    • synchronized(Obj){}
    • Obj 称之为同步监视器
      • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
      • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象的本身,或者是 class [反射中讲解]
    • 同步监视器的执行过程
      • 第一个线程访问,锁定同步监视器,执行其中代码
      • 第二个线程访问,发现同步监视器被锁定,无法访问
      • 第一个线程访问完毕,解锁同步监视器
      • 第二线程访问,发现同步监视器没有锁,然后锁定并访问
    案例一:安全的火车站买票
    synchronized 同步方法,锁的是this,也就是BuyTicket的对象
     1 package com.thread.syn;
     2 
     3 public class SafeBuyTicket {
     4 
     5     public static void main(String[] args) {
     6         BuyTicket station = new BuyTicket();
     7 
     8         new Thread(station, "苦逼的我").start();
     9         new Thread(station, "牛逼的你们").start();
    10         new Thread(station, "可恶的黄牛党").start();
    11 
    12     }
    13 }
    14 
    15 
    16 class BuyTicket implements Runnable {
    17 
    18     //
    19     private int ticketNums = 10;
    20     boolean flag = true;//外部停止方式
    21 
    22     @Override
    23     public void run() {
    24 
    25         //买票
    26         while (flag) {
    27             try {
    28                 buy();
    29             } catch (InterruptedException e) {
    30                 e.printStackTrace();
    31             }
    32         }
    33     }
    34 
    35     //synchronized 同步方法,锁的是this
    36     private synchronized void buy() throws InterruptedException {
    37         //判断是否有票
    38         if (ticketNums <= 0) {
    39             flag = false;
    40             return;
    41         }
    42 
    43         //模拟延时
    44         Thread.sleep(100);
    45         //买票
    46         System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    47     }
    48 
    49 }
    50 
    51 结果:
    52 苦逼的我拿到10
    53 苦逼的我拿到9
    54 苦逼的我拿到8
    55 苦逼的我拿到7
    56 苦逼的我拿到6
    57 苦逼的我拿到5
    58 苦逼的我拿到4
    59 牛逼的你们拿到3
    60 牛逼的你们拿到2
    61 牛逼的你们拿到1
    案例二:银行取钱
    synchronized默认是锁this,而this锁不住,因为它操作的是账户,不是银行。就需要用到同步块。
    sychronized(Obj){},锁的对象是变化的量,需要增删改的对象
     1 package com.thread.syn;
     2 
     3 public class SafeBank {
     4     public static void main(String[] args) {
     5         //账户
     6         Account account = new Account(100, "结婚基金");
     7 
     8         Drawing you = new Drawing(account, 50, "你");
     9         Drawing girlFriend = new Drawing(account, 100, "girlFriend");
    10 
    11         you.start();
    12         girlFriend.start();
    13 
    14     }
    15 }
    16 
    17 
    18 //账户
    19 class Account {
    20     int money;//余额
    21     String name;//卡名
    22 
    23     public Account(int money, String name) {
    24         this.money = money;
    25         this.name = name;
    26     }
    27 }
    28 
    29 //银行:模拟取款
    30 class Drawing extends Thread {
    31 
    32     //账户
    33     Account account;
    34     //取了多少钱
    35     int drawingMoney;
    36     //现在手里有多少钱
    37     int nowMoney;
    38 
    39     public Drawing(Account account, int drawingMoney, String name) {
    40         super(name);
    41         this.account = account;
    42         this.drawingMoney = drawingMoney;
    43     }
    44 
    45     //取钱
    46     //synchronized 默认锁的是this.
    47     @Override
    48     public void run() {
    49         //锁的对象就是变化的量,需要增删改的对象
    50         synchronized (account) {
    51             //判断有没有钱
    52             if (account.money - drawingMoney < 0) {
    53                 System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
    54                 return;
    55             }
    56 
    57             try {
    58                 Thread.sleep(100);
    59             } catch (InterruptedException e) {
    60                 e.printStackTrace();
    61             }
    62 
    63             //卡内余额 = 余额 - 你取得钱
    64             account.money = account.money - drawingMoney;
    65             //你手里的钱
    66             nowMoney = nowMoney + drawingMoney;
    67 
    68             System.out.println(account.name + "余额为:" + account.money);
    69             //Thread.currentThread().getName() = this.getName()
    70             System.out.println(this.getName() + "手里的钱:" + nowMoney);
    71 
    72         }
    73     }
    74 
    75 }
    76 结果:
    77 结婚基金余额为:50
    78 你手里的钱:50
    79 girlFriend钱不够,取不了
    案例三:线程安全的集合
    把list对象放进同步代码块,锁住即可
     1 package com.thread.syn;
     2 
     3 import java.util.ArrayList;
     4 import java.util.List;
     5 
     6 public class SafeList {
     7 
     8     public static void main(String[] args) {
     9 
    10         List<String> list = new ArrayList<>();
    11         for (int i = 0; i < 10000; i++) {
    12             new Thread(() -> {
    13                 synchronized (list){
    14                     list.add(Thread.currentThread().getName());
    15                 }
    16             }).start();
    17         }
    18         try {
    19             Thread.sleep(3000);
    20         } catch (InterruptedException e) {
    21             e.printStackTrace();
    22         }
    23         System.out.println(list.size());
    24     }
    25 }
    26 
    27 结果:
    28 10000
  • 相关阅读:
    Asp.Net中virtual、override理解
    SQL 知道字段名 全表搜索此字段属于哪个表
    C#中(int)a和Convert.ToInt32(a)的区别
    sql 查询表共多少列
    理解字节和字符
    C# IIS7.0+ Web.Config 配置Session过期时间
    Java版的扫雷游戏源码
    Activiti6.0教程 28张表解析 (三)
    Activiti6.0教程 Service用途剖析 (二)
    Activiti6.0教程 Eclipse安装Activiti Diagram插件(一)
  • 原文地址:https://www.cnblogs.com/duanfu/p/12260763.html
Copyright © 2020-2023  润新知