• 6.10Java线程同步_synchronized


    6.10Java线程同步_synchronized

    线程的同步的含义

    线程的同步是指:

    • 在一个多线程环境下保证数据的准确性、安全性--->线程安全

    • 提升性能--->三高原则(高性能、高并发、高可用)

    难点:

    • 线程的同步不够形象

    • 并发控制比较困难--->琢磨、分析、又快又好

    什么是并发?

    并发是指:

    • 同一个对象多个线程同时操作--->同一个银行账户取钱

    Java线程不安全演示--->相同资源数、负数资源数
    package iostudy.synchro;

    /**
    * 线程不安全的演示
    * @since JDK 1.8
    * @date 2021/6/10
    * @author Lucifer
    */
    public class UnsafeTestNo1 {
       public static void main(String[] args) {

           /*实例化实现类对象--->一份资源对象*/
           UnsafeWeb12306 unsafeWeb12306 = new UnsafeWeb12306();

           /*实例化多个代理商--->多个线程*/
           new Thread(unsafeWeb12306, "小马哥!").start();
           new Thread(unsafeWeb12306, "大马哥!").start();
           new Thread(unsafeWeb12306, "老马哥!").start();
           /*
           1、这样写线程会导致线程不安全,有负数的情况和重复值的情况
           2、可能会出现两个99和值-1、-2
            */

      }
    }

    /**
    * 创建一个内部多线程类
    */
    class UnsafeWeb12306 implements Runnable{

       /*设置资源数--->票数*/
       private int ticketNums = 99;
       /*开关*/
       private boolean flag = true;

       /*重写Runnable接口当中的run方法*/
       @Override
       public void run(){

           /*线程的实际流程*/
           while (flag){
               test();
          }

      }

       /**
        * 封装一个线程内部具体方法类
        */
       public 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--);

      }
    }

    说明:

    出现负数票数情况

    这里面涉及到边界值的概念

    • 同一时间片a、b、c三个线程都进入到多线程环境中(没有线程池)

    • b先进入,持有资源等待

    • b先拿走资源--->默认用户线程要执行完毕

    • a、c也需要执行完毕,只能拿走0、-1

    关键点是没有控制最后一份资源而jvm默认所有线程不经过设置都是用户线程

    出现相同票数的情况
    • new一个新生状态的线程的时候会开辟一个工作空间

    • 每一个线程的工作空间只与主存交互,各线程之间不存在数据交互

    • 在主存数据发生修改之前有两个线程与主存的数据进行交互,同时从主存拷贝值拿到工作空间进行处理导致出现两个一样的值

    银行取款线程不安全
    package iostudy.synchro;

    /**
    * 模拟银行取款线程不安全的情况
    * 1、同一账户
    * 2、多人同时操作
    * 同时取出
    * 一边取出一边存入
    * 会造成数据的不一致
    * @since JDK 1.8
    * @date 2021/6/10
    * @author Lucifer
    */
    public class UnsafeTestNo2 {
       public static void main(String[] args) {
           /*创建资源类对象*/
           Account account = new Account(100, "彩礼");

           /*两个代理类对象--->三个参数(账户、取出数目、取出的人员*/
           Drawing you = new Drawing(account, 80,  "你");
           Drawing she = new Drawing(account, 90, "她");

           /*调用start方法--->就绪状态*/
           you.start();
           she.start();

      }
    }

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

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

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

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

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

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

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

           /*在存钱和取钱的时候加入条件*/
           if (account.money-drawingMoney<0){
               /*结束方法*/
               return;
          }

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

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

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

      }
    }
    多线程操作容器保证数据不被丢失、覆盖
    package iostudy.synchro;

    import java.util.ArrayList;
    import java.util.List;

    /**
    * 线程不安全:容器操作
    * @since JDK 1.8
    * @date 2021/6/10
    * @author Lucifer
    */
    public class UnsafeTestNo3 {
       public static void main(String[] args) {

           /*新建容器*/
           List<String> list = new ArrayList<String>();

           /*开启一万个线程*/
           for (int i=0; i<10000; i++){
               /*使用lambda表达式进行添加*/
               new Thread(() -> {
                   list.add(Thread.currentThread().getName());
              }).start();
          }

           /*打印容器结果*/
           System.out.println(list.size());
           /*
           其结果是5105,并没有到1W条数据
           这是线程不安全的具体表现
           不是所有的情况下都需要考虑线程安全
           1、对数据存在"改"的行为--->控制
           2、存在读--->拷贝,不需要保证线程安全
            */

      }
    }

     

    It's a lonely road!!!
  • 相关阅读:
    二叉树的存储结构
    面试Java需要的知识总结
    EJB总结
    WEB 容器、WEB服务和应用服务器的区别与联系
    Linux安装JBOSS
    JBOSS和WebLogic区别
    深入浅出JMS(一)--JMS基本概念
    Java缓冲流细节
    xor和路径(codevs 2412)
    外星千足虫(bzoj 1923)
  • 原文地址:https://www.cnblogs.com/JunkingBoy/p/14872838.html
Copyright © 2020-2023  润新知