zoukankan      html  css  js  c++  java
  • JavaSE:线程同步机制

    线程同步机制(重点)

    1.  基本概念

        <1>  当多个线程同时访问同一种共享资源时,可能会造成数据的覆盖等不一致性问题,

              此时就需要对线程之间进行通信和协调,该机制就叫做线程的同步机制

        <2>  多个线程并发读写同一个临界资源时,会发生线程并发安全问题

        <3>  异步操作:多线程并发的操作,各自独立运行

        <4>  同步操作:多线程串行的操作,先后执行的顺序

    2.  案例代码 (银行的存款取款)

    <1>AccountRunnableTest.java

     

     

     会出现问题的情况(两个线程同时,对同一个账户,进行取款操作):

     

    原因: 线程一没来得及执行setBalance(),线程二就已经执行了

    因此,需要线程的同步机制。

    3.   案例分析 (银行的存款取款)

        <1>  当两个线程同时对同一个账户进行取款时,可能会导致最终的账户余额不合理

        <2>  引发原因:线程一执行取款时,还没来得及将取款后的余额写入后台,线程二就已经开始取款

        <3>  解决方案:线程一完成取款操作后,再让线程二执行即可,将线程的并发操作改为串行操作

        <4>  经验: 在开发中,尽量减少串行操作的范围,从而提高效率  

    4.  解决方法

    方法一:

     不使用方法一,因为这样做,会使得多线程 (线程t1、线程t2同时运行) 失效。

    方法二:使用 同步锁  / 监视器, 如下

    5.  线程同步的实现方式 (synchronized关键字)

        使用synchronized关键字,实现同步 / 对象锁机制,从而保证线程执行的原子性,具体方式如下:

           <1>使用同步代码块的方式,实现部分代码的锁定,格式如下:

                synchronized (类类型的引用){

                  编写所有需要锁定的代码;

                }

           <2>使用同步方法的方式,实现所有代码的锁定

              直接使用synchronized关键字来修饰整个方法即可

              该方法等价于:

                synchronized(this) {整个方法体的代码}

    6.  synchronized 的使用 (使用同步代码块的方式)

     

     

     注意:

    7.  另外,在AccountThreadTest.java中,(使用同步代码块的方式),设置同步锁需要注意:

     1 package com.lagou.task18;
     2 
     3 public class AccountThreadTest extends Thread {
     4     private int balance; // 用于描述账户的余额
     5     private static Demo dm = new Demo(); // 隶属于类层级,所有对象共享同一个
     6 
     7     public AccountThreadTest() {
     8     }
     9 
    10     public AccountThreadTest(int balance) {
    11         this.balance = balance;
    12     }
    13 
    14     public int getBalance() {
    15         return balance;
    16     }
    17 
    18     public void setBalance(int balance) {
    19         this.balance = balance;
    20     }
    21 
    22     @Override
    23     public /*static*/ /*synchronized*/ void run() {
    24         /*System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
    25         //synchronized (dm) { // ok
    26             //synchronized (new Demo()) { // 锁不住  要求必须是同一个对象
    27             // 1.模拟从后台查询账户余额的过程
    28             int temp = getBalance(); // temp = 1000  temp = 1000
    29             // 2.模拟取款200元的过程
    30             if (temp >= 200) {
    31                 System.out.println("正在出钞,请稍后...");
    32                 temp -= 200;  // temp = 800   temp = 800
    33                 try {
    34                     Thread.sleep(5000);
    35                 } catch (InterruptedException e) {
    36                     e.printStackTrace();
    37                 }
    38                 System.out.println("请取走您的钞票!");
    39             } else {
    40                 System.out.println("余额不足,请核对您的账户余额!");
    41             }
    42             // 3.模拟将最新的账户余额写入到后台
    43             setBalance(temp); // balance = 800  balance = 800
    44         //}*/
    45         test();
    46     }
    47 
    48     public /*synchronized*/ static void test() {
    49         synchronized (AccountThreadTest.class) { // 该类型对应的Class对象,由于类型是固定的,因此Class对象也是唯一的,因此可以实现同步
    50             System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
    51             //synchronized (dm) { // ok
    52             //synchronized (new Demo()) { // 锁不住  要求必须是同一个对象
    53             // 1.模拟从后台查询账户余额的过程
    54             int temp = 1000; //getBalance(); // temp = 1000  temp = 1000
    55             // 2.模拟取款200元的过程
    56             if (temp >= 200) {
    57                 System.out.println("正在出钞,请稍后...");
    58                 temp -= 200;  // temp = 800   temp = 800
    59                 try {
    60                     Thread.sleep(5000);
    61                 } catch (InterruptedException e) {
    62                     e.printStackTrace();
    63                 }
    64                 System.out.println("请取走您的钞票!");
    65             } else {
    66                 System.out.println("余额不足,请核对您的账户余额!");
    67             }
    68             // 3.模拟将最新的账户余额写入到后台
    69             //setBalance(temp); // balance = 800  balance = 800
    70         }
    71     }
    72 
    73     public static void main(String[] args) {
    74 
    75         AccountThreadTest att1 = new AccountThreadTest(1000);
    76         att1.start();
    77 
    78         AccountThreadTest att2 = new AccountThreadTest(1000);
    79         att2.start();
    80 
    81         System.out.println("主线程开始等待...");
    82         try {
    83             att1.join();
    84             //t2.start(); // 也就是等待线程一取款操作结束后再启动线程二
    85             att2.join();
    86         } catch (InterruptedException e) {
    87             e.printStackTrace();
    88         }
    89         System.out.println("最终的账户余额为:" + att1.getBalance()); // 800
    90 
    91     }
    92 
    93     }

    8. 使用同步方法的方式,实现所有代码的锁定 (AccountRunnableTest.java)

     

    9. 使用同步方法的方式,实现所有代码的锁定 (AccountThreadTest.java)

     用synchronized修饰staic方法,等价于:

     

  • 相关阅读:
    POJ 1236 Network of Schools(强连通分量缩点求根节点和叶子节点的个数)
    文本编辑器vim和gedit
    Ubuntu安装tensorflow
    剑指offer——python【第29题】最小的K个数
    剑指offer——python【第30题】连续子数组的最大和
    剑指offer——python【第37题】数字在排序数组中出现的次数
    剑指offer——python【第28题】数组 中出现次数超过一半的数字
    剑指offer——python【第31题】整数1出现的次数
    剑指offer——python【第54题】字符流中第一个不重复的字符
    剑指offer——python【第40题】数组中只出现一次的数字
  • 原文地址:https://www.cnblogs.com/JasperZhao/p/14892192.html
Copyright © 2011-2022 走看看