一、进程:(QQ)
1、程序(任务)的执行过程
2、持有资源(共享内存,共享文件)和线程
二、线程:(文字聊天、收发文件)
三、线程之间的交互:
1、同步:协同完成某个进程
2、互斥:资源的使用
四、java对线程的支持:
1、java对线程的支持
1> Thread
2> Runnable
public void run();方法提供线程实际运行时执行的内容
package com.zy.concurrent; public class Actor extends Thread { // 覆写run方法 public void run() { System.out.println(getName() + "是一个演员!"); int count = 0; boolean keepRunning = true; while (keepRunning) { System.out.println(getName() + "登台演出:" + (++count)); if (count == 100) { keepRunning = false; } if (count % 10 == 0) { try { // 线程休眠1秒钟 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println(getName() + "的演出结束了!"); } public static void main(String[] args) { Thread actor = new Actor(); actor.setName("Mr.Thread"); actor.start(); Thread actressThread = new Thread(new Actress(),"Ms.Runnable"); actressThread.start(); // cpu智能运行一个线程,当一个线程休眠的时候,另外一个线程才获得运行的机会 } } class Actress implements Runnable { // 覆写run方法 @Override public void run() { System.out.println(Thread.currentThread().getName() + "是一个演员!"); int count = 0; boolean keepRunning = true; while (keepRunning) { System.out.println(Thread.currentThread().getName() + "登台演出:" + (++count)); if (count == 100) { keepRunning = false; } if (count % 10 == 0) { try { // 线程休眠1秒钟 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println(Thread.currentThread().getName() + "的演出结束了!"); } }
package com.zy.concurrent.base; /** * 隋唐演义大戏舞台 */ public class Stage extends Thread { public void run() { System.out.println("欢迎观看隋唐演义"); // 让舞台线程休眠一下,等观众准备好 try { Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("大幕徐徐拉开"); try { Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("话说隋朝末年,隋军与农民起义军杀得浑天暗地..."); ArmyRunnable armyTaskOfSuiDynasty = new ArmyRunnable(); ArmyRunnable armyTaskOfRevolt = new ArmyRunnable(); // 使用Runnable接口创建线程 Thread armyOfSuiDynasty = new Thread(armyTaskOfSuiDynasty, "隋军"); Thread armyOfRevolt = new Thread(armyTaskOfRevolt, "农民起义军"); // 启动线程,让军队开始作战 armyOfSuiDynasty.start(); armyOfRevolt.start(); // 舞台线程休眠,便于观看军队厮杀 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } armyTaskOfSuiDynasty.keepRunning = false; armyTaskOfRevolt.keepRunning = false; try { armyOfRevolt.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("正当双方激战正酣,半路杀出了个程咬金"); Thread mrChen = new KeyPersonThread(); mrChen.setName("程咬金"); System.out.println("程咬金的立项就是结束战争,使百姓安居乐业!"); // 停止军队作战 // 停止线程的方法 armyTaskOfSuiDynasty.keepRunning = false; armyTaskOfRevolt.keepRunning = false; // 使军队作战真实停下来,舞台资源让给关键人物 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 历史大戏留给关键人物 mrChen.start(); // 所有人物等关键人物完成历史使命 try { mrChen.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("战争结束,人们安居乐业,程先生实现了人生梦想"); System.out.println("隋唐演义结束,谢谢观看!"); } public static void main(String[] args) { new Stage().start(); } }
package com.zy.concurrent.base; //军队线程 //模拟作战双方的行为 public class ArmyRunnable implements Runnable { // volatile保证了线程可以正确的读取其他线程写入的值(军队停止进攻不是军队自己下达的命令,而是别的线程) // 可见性 ref JMM;java内存模型 volatile boolean keepRunning = true; @Override public void run() { while (keepRunning) { // 发动五连击 for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "进攻对方[" + i + "]"); // 使线程让出当前cpu处理器资源,再所有线程同时去竞争cpu资源 // 让出了处理器时间,下次该是谁进攻还不一定呢! Thread.yield(); } } System.out.println(Thread.currentThread().getName() + "结束了战斗!"); } }
package com.zy.concurrent.base; public class KeyPersonThread extends Thread { // 覆写关键人物的run方法,关键人物业务代码 public void run() { System.out.println(Thread.currentThread().getName() + "开始了战斗!"); // 普通军队只能进行5连击的攻击,而关键人物可以进行10连击的攻击 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "左突右杀,攻击隋军..."); } System.out.println(Thread.currentThread().getName() + "结束了战斗!"); } }
volatile boolean keepRunning = true;
package com.zy.concurrent.racecondition; /** * 宇宙的能量系统 遵循能量守恒定律 能量不会凭空创生或消失,只会从一处转到另一处 * */ public class EnergySystem { // 能量盒子,能量存储的地方 private final double[] energyBoxes; /** * 能量总值 * * @param n 能量盒子的数量 * @param initalEnergy 每个能量盒子初始含有的能量值 */ public EnergySystem(int n, double initalEnergy) { energyBoxes = new double[n]; for (int i = 0; i < energyBoxes.length; i++) { energyBoxes[i] = initalEnergy; } } public void transfer(int from, int to, double amount) { // 能量转出的单元能量不足时,终止本次操作 if (energyBoxes[from] < amount) return; System.out.print(Thread.currentThread().getName()); energyBoxes[from] -= amount; // System.out.printf(),java对应格式输出 System.out.printf("从%d转移%10.2f单位能量到%d",from, amount, to); energyBoxes[to] += amount; System.out.printf("能量总和:%10.2f%n",getTotalEnergies()); } /** * 返回能量盒子能量的总和 * * @return */ public double getTotalEnergies() { double sum = 0; for (double amount : energyBoxes) { sum += amount; } return sum; } /** * 返回能量盒子的长度 * * @return */ public int getBoxAmount() { return energyBoxes.length; } }
package com.zy.concurrent.racecondition; public class EnergyTransferTask implements Runnable { // 共享的能量世界 private EnergySystem energySystem; // 能量转移的源能量盒子下标 private int fromBox; // 单次能量转移的最大单元 private double maxAmount; // 最大休眠时间(毫秒) private int DELAY = 10; public EnergyTransferTask(EnergySystem energySystem, int from, double max) { this.energySystem = energySystem; this.fromBox = fromBox; this.maxAmount = max; } // while模拟宇宙中永不停止的能量转移 @Override public void run() { try { while (true) { int toBox = (int)(energySystem.getBoxAmount() * Math.random()); double amount = maxAmount * Math.random(); energySystem.transfer(fromBox,toBox, amount); Thread.sleep((int) (DELAY * Math.random())); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.zy.concurrent.racecondition; public class EnergySystemTest { // 将要构建的能量世界中能量盒子数量 public static final int BOX_AMOUNT = 100; // 每个盒子初始能量 public static final double INITAL_ENERGY = 1000; public static void main(String[] args) { EnergySystem eng = new EnergySystem(BOX_AMOUNT,INITAL_ENERGY); for (int i = 0; i < BOX_AMOUNT; i++) { EnergyTransferTask task = new EnergyTransferTask(eng, i, INITAL_ENERGY); Thread t = new Thread(task,"TransferThread_" + i); t.start(); } } }
当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏,这种现象称为争用条件.
线程1先从能量盒子导出对应位置的能量值5000,线程1未将转换后的能量值5500写入能量盒子,能量值5500存在线程1的缓存中,执行时间就耗尽了,这时线程2开始执行,把线程1在能量盒子所对应位置的能量值5000导出,然后把转换后的能量值5900写入,此时能量盒子对应位置的能量值就是5900,线程2执行时间 耗尽,这时线程1又有机会执行,将缓存中的能量值5500写入,此时就把线程2写入的能量值5900覆盖了.
二、互斥与同步
互斥:同一时间只有一个线程对临界区进行操作
同步:通信机制,告诉其他线程,本线程已执行完临界区
实现互斥:
1、增加一个锁对象
private final Object lockObj = new Object();
synchronized (lockObj) { }
public void transfer(int from, int to, double amount) { synchronized (lockObj) { // 能量转出的单元能量不足时,终止本次操作 if (energyBoxes[from] < amount) return; System.out.print(Thread.currentThread().getName()); energyBoxes[from] -= amount; // System.out.printf(),java对应格式输出 System.out.printf("从%d转移%10.2f单位能量到%d", from, amount, to); energyBoxes[to] += amount; System.out.printf("能量总和:%10.2f%n", getTotalEnergies()); } }
解决办法:当这个条件不满足时,也就是当转移的能量源不足时,应该让线程去等待,直到有足够能量去转移,降低线程去获取锁的开销, 提高整体的性能.(下方的Wait Set讲解)
// 能量转出的单元能量不足时,终止本次操作 // if (energyBoxes[from] < amount) // return; // while循环,保证条件不满足时任务会被条件阻挡 // 而不是继续竞争cpu资源 while (energyBoxes[from] < amount) { try { // 使线程进入等待的状态,避免线程去持续的申请锁 lockObj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
// 唤醒所有在lockObj对象上等待的线程(同步) lockObj.notifyAll();