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!!!
原文地址:https://www.cnblogs.com/JunkingBoy/p/14878072.html