zoukankan      html  css  js  c++  java
  • 6.11Java多线程、并发、同步、synchronized方法

    6.11Java多线程、并发、同步、synchronized方法

    摸索前进

    package iostudy.synchro;

    /**
    * 多线程并发、同步保证数据准确性,效率尽可能高和好
    * 线程安全:
    * 1、在并发池保证数据的准确性
    * 2、同时保证效率尽可能高
    * synchronized
    * 1、同步方法
    * 2、同步块
    * @since JDK 1.8
    * @date 2021/6/11
    * @author Lucfier
    */
    public class SynTestNo1 {
       public static void main(String[] args) {

           /*创建资源类对象*/
           SafeWeb12306 safeWeb12306 = new SafeWeb12306();

           /*多个线程*/
           new Thread(safeWeb12306,  "fe斯特").start();
           new Thread(safeWeb12306, "赛肯").start();
           new Thread(safeWeb12306, "瑟得").start();
      }
    }

    /**
    * 创建一个资源类,测试同步方法
    */
    class SafeWeb12306 implements Runnable{
       /*票数*/
       private int ticketNums = 99;
       /*设置开关*/
       private boolean flag = true;
       /*重写run方法--->多线程执行入口*/
       @Override
       public void run(){
           while (flag){
               /*在下面写线程的具体执行方法*/
               test();
          }
      }

       /**
        * 封装一个线程内部具体方法类
        */
       public synchronized void test(){

           /*判断资源数、改变开关、结束方法*/
           //每次方法一开始就执行判断
           if (ticketNums<0){
               /*改变开关状态*/
               flag = false;
               /*结束方法*/
               return;
          }

           /*模拟延时*/
           try {
               /*线程携带数据锁休眠*/
               Thread.sleep(200);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }
           /*打印当前线程对象信息--->获取当前线程方法*/
           System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

      }
    }

    之前出现负数资源的原因

    • 最后一位资源为1

    • 三个线程同时访问该资源(A,B,C)

    • B线程先运行,但是B不<=0,所以进行延时等待。B没有修改数据

    • 此时A和C也进入线程体。此时B刚好修改数据而A和C访问到的资源数还是1

    出现线程访问到相同资源数的原因

    • 每个线程都有一个工作空间与主存交互

    • 线程会round(加载)、store(存储)

    • 当B线程没有将工作空间的修改数据返回主存的时候A、C线程也对主存的数据拷贝了一份

    • 所以还是原来的资源数而不是更新后的资源数

    这样就实现了同步,(线程大概率按顺序访问)

    synchronized方法实例
    package iostudy.synchro;

    /**
    * 多线程并发、同步保证数据准确性,效率尽可能高和好
    * 线程安全:
    * 1、在并发池保证数据的准确性
    * 2、同时保证效率尽可能高
    * synchronized
    * 1、同步方法
    * 2、同步块
    * @since JDK 1.8
    * @date 2021/6/11
    * @author Lucfier
    */
    public class SynTestNo1 {
       public static void main(String[] args) {

           /*创建资源类对象*/
           SafeWeb12306 safeWeb12306 = new SafeWeb12306();

           /*多个线程*/
           new Thread(safeWeb12306,  "fe斯特").start();
           new Thread(safeWeb12306, "赛肯").start();
           new Thread(safeWeb12306, "瑟得").start();
      }
    }

    /**
    * 创建一个资源类,测试同步方法
    */
    class SafeWeb12306 implements Runnable{
       /*票数*/
       private int ticketNums = 1000;
       /*设置开关*/
       private boolean flag = true;
       /*重写run方法--->多线程执行入口*/
       @Override
       public void run(){
           while (flag){

               /*模拟延时*/
               try {
                   /*线程携带数据锁休眠*/
                   Thread.sleep(200);
              }catch (InterruptedException e){
                   System.out.println(e.getMessage());
                   e.printStackTrace();
              }

               /*在下面写线程的具体执行方法*/
               test();

          }
      }

       /**
        * 封装一个线程内部具体方法类
        */
       public synchronized void test(){

           /*判断资源数、改变开关、结束方法*/
           //每次方法一开始就执行判断
           if (ticketNums<0){
               /*改变开关状态*/
               flag = false;
               /*结束方法*/
               return;
          }

           /*打印当前线程对象信息--->获取当前线程方法*/
           System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

      }
       /*
       1、该资源的锁操作了两个属性
           1、ticketNums
           2、flag
       该方法是成员方法,操作的都是对象(this)SafeWeb12306--->锁的是对象
       方法当中的资源必须是对象的资源才能使用,非该对象的资源无法锁住
       使用方法的时候判断对象是否能使用,有了锁就无法使用对象--->锁了对象的资源(方法当中都是与对象相关的资源。如果不是就可能是锁失败了)
        */
    }
    synchronized锁定目标不对(锁定对象不对),导致锁定失败

    AccountTest类:

    package iostudy.synchro;

    /**
    * 创建一个账户类--->资源类
    * @since JDK 1.8
    * @date 2021/6/10
    * @author Lucifer
    */
    public class AccountTest {

           /*定义资源属性*/
           int money; //金额
           String name; //名称字符串

           /*创建构造器*/
           public AccountTest(int money, String name) {
               this.money = money;
               this.name = name;
          }

    }

    锁定错误的线程同步类:

    package iostudy.synchro;

    public class SynTestNo2 {

       /*定义资源属性*/
       int money; //金额
       String name; //名称字符串

       public static void main(String[] args) {

           AccountTest accountTest = new AccountTest(100, "money");
           SafeDrawing you = new SafeDrawing(accountTest, 80, "Lucifer");
           SafeDrawing she = new SafeDrawing(accountTest, 90, "JunkingBoy");
           you.start();
           she.start();

      }

       /*创建构造器*/
       public SynTestNo2(int money, String name) {
           this.money = money;
           this.name = name;
      }

    }

    /**
    * 模拟提款机提款类--->多线程
    * @since JDK 1.8
    * @date 2021/6/10
    * @author Lucifer
    */
    class SafeDrawing extends Thread{

       /*创建实现类对象--->面向对象的思想*/
       AccountTest accountTest; //取出的账户
       int drawingMoney; //取出的钱数
       int pocketTotal; //取出的钱的总数

       /*创建构造器,将属性定义为参数*/
       public SafeDrawing(AccountTest accountTest, int drawingMoney, String name) {
           super(name); //线程的名称
           this.accountTest = accountTest;
           this.drawingMoney = drawingMoney;
      }

       /*重写run方法--->线程的具体实现*/
       @Override
       public void run() {
           test();
      }

       //目标锁定不对,锁定失败
       /*
       这里不是锁定this,而应该锁定account
        */
       public synchronized void test(){
           /*在存钱和取钱的时候加入条件*/
           if (accountTest.money-drawingMoney<0){
               /*结束方法*/
               return;
          }

           /*模拟取款的网络延迟*/
           try {
               Thread.sleep(1000);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }
           /*
           理论上说加了判断就会控制结果
           但是实际上不会,这个需要对资源+锁实现控制的效果
            */

           /*账户的金额-取出的金额*/
           accountTest.money -= drawingMoney;
           /*口袋中的金额+取出的钱*/
           pocketTotal += drawingMoney;

           /*因为继承了父类所以可以直接用this--->获取线程的名称*/
           System.out.println(this.getName() + "--->账户余额为:" + accountTest.money);
           System.out.println(this.getName() + "--->身上余额为:" + pocketTotal);

      }
       /*
       该方法的this是取款机的this,应该锁住的资源是AccountTest类的对象
        */
    }
    It's a lonely road!!!
  • 相关阅读:
    tensorflow 2.0 学习 (十) 拟合与过拟合问题
    tensorflow 2.0 学习 (九) tensorboard可视化功能认识
    tensorflow 2.0 学习 (八) keras模块的认识
    tensorflow 2.0 学习 (七) 反向传播代码逐步实现
    tensorflow 2.0 学习 (六) Himmelblua函数求极值
    tensorflow 2.0 学习 (五)MPG全连接网络训练与测试
    arp协议简单介绍
    Pthread spinlock自旋锁
    线程和进程状态
    内核态(内核空间)和用户态(用户空间)的区别和联系·
  • 原文地址:https://www.cnblogs.com/JunkingBoy/p/14878072.html
Copyright © 2011-2022 走看看