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!!!
  • 相关阅读:
    SQL数据类型详解
    将Excel表格导入DataTable的方法
    .net的反射机制
    经典SQL语句大全(一)
    c# Invoke和BeginInvoke 区别
    c#中两种常用的异步调用方法
    SQL存储过程参数问题
    API 函数大全(下)
    API函数大全 (上)
    javascript 常用function
  • 原文地址:https://www.cnblogs.com/JunkingBoy/p/14878072.html
Copyright © 2011-2022 走看看