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线程也对主存的数据拷贝了一份
所以还是原来的资源数而不是更新后的资源数
这样就实现了同步,(线程大概率按顺序访问)
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--->锁的是对象 方法当中的资源必须是必须是对象的资源才能使用,非该对象的资源无法锁住 使用方法的时候判断对象是否能使用,有了锁就无法使用对象--->锁了对象的资源(方法当中都是与对象相关的资源。如果不是就可能是锁失败了) */ }
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类的对象 */ }