课程地址:深入浅出Java多线程
课程讲师:Arthur
进程
-
程序(任务)的执行过程 (动态性-当双击运行)
-
持有资源(共享内存,共享文件)和线程(进程是资源的载体,也是线程的载体,脱离进程去谈论线程没有意义;)
-
线程是系统最小的执行单元
-
同一进程中有多个线程
-
线程共享进程的资源
线程交互方式:互斥,同步。
011.png/**
* 军队线程
* 模拟作战双方的行为
*/
public class ArmyRunnable implements Runnable {
//volatile保证了线程可以正确的读取其他线程写入的值
//可见性 ref JMM, happens-before原则
volatile boolean keepRunning = true;
@Override
public void run() {
while(keepRunning){
//发动5连击
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"进攻对方["+i+"]");
//让出了处理器时间,下次该谁进攻还不一定呢!
Thread.yield();
}
}
System.out.println(Thread.currentThread().getName()+"结束了战斗!");
}
}
/**
* 英雄任务
*/
public class KeyPersonThread extends Thread {
public void run(){
System.out.println(Thread.currentThread().getName()+"开始了战斗!");
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"左突右杀,攻击隋军...");
}
System.out.println(Thread.currentThread().getName()+"结束了战斗!");
}
}
/**
* 隋唐演义大戏舞台
*/
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();
}
System.out.println("正当双方激战正酣,半路杀出了个程咬金");
Thread mrCheng = new KeyPersonThread();
mrCheng.setName("程咬金");
System.out.println("程咬金的理想就是结束战争,使百姓安居乐业!");
//停止军队作战
//停止线程的方法
armyTaskOfSuiDynasty.keepRunning = false;
armyTaskOfRevolt.keepRunning = false;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 历史大戏留给关键人物
*/
mrCheng.start();
//万众瞩目,所有线程等待程先生完成历史使命
try {
mrCheng.join(); // 所有线程会等待调用join()方法的这个线程执行完成后,再执行后续内容
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生梦想,为人民作出了贡献!");
System.out.println("谢谢观看隋唐演义,再见!");
}
public static void main(String[] args) {
new Stage().start();
}
}
如何正确的停止Java中的线程
-
废弃stop()方法
-
使用退出标志
-
interrupt()方法初衷并不是用于停止线程,isInterrupted() 和 interrupted()测试当前线程是否被中断
public class WrongWayStopThread extends Thread{
public static void main(String[] args){
WrongWayStopThread thread = new WrongWayStopThread();
System.out.println("Starting thread ...");
thread.start() ;
try{
Threaf.sleep(3000) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
System.out.println("Interrupting thread ...");
thread.interrupt() ; // 并不能使程序停下来
try{
Threaf.sleep(3000) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
System.out.println("Stopping thread ...");
}
public void run(){
// while(true){
while(!this.isInterrupted()){ // 线程停下,其实相当于退出旗标的方法
System.out.println("Thread is running ...");
/* long time = System.currentTimeMillis() ;
while((System.currentTimeMillis() - time) < 1000){
// 减少屏幕输入的空循环
}
*/
// 换成等效代码,线程不能正常结束,且抛出异常。原因是:当现场调用某些方法进入阻塞状态,此时该线程再被调用interrupt()方法,会产生两个结果,第一是中断状态被清除(this.isInterrupted()),线程的isInterrupted()方法不能返回表示是否被中断的正确状态;第二是sleep()方法会收到一个InterruptedException异常
Threaf.sleep(3000) ;
}
}
}
推荐使用退出旗标的方式退出线程
线程的交互
/**
* 宇宙的能量系统
* 遵循能量守恒定律:
* 能量不会凭空创生或消失,只会从一处转移到另一处
*/
public class EnergySystem {
//能量盒子,能量存贮的地方
private final double[] energyBoxes;
private final Object lockObj = new Object(); // 锁对象
/**
*
* @param n 能量盒子的数量
* @param initialEnergy 每个能量盒子初始含有的能量值
*/
public EnergySystem(int n, double initialEnergy){
energyBoxes = new double[n];
for (int i = 0; i < energyBoxes.length; i++)
energyBoxes[i] = initialEnergy;
}
/**
* 能量的转移,从一个盒子到另一个盒子
* @param from 能量源
* @param to 能量终点
* @param amount 能量值
*/
public void transfer(int from, int to, double amount){
synchronized(lockObj){
// if (energyBoxes[from] < amount)
// return;
//while循环,保证条件不满足时任务都会被条件阻挡
//而不是继续竞争CPU资源
// Wait set
while (energyBoxes[from] < amount){
try {
//条件不满足, 将当前线程放入Wait Set
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
energyBoxes[from] -= amount;
System.out.printf("从%d转移%10.2f单位能量到%d", from, amount, to);
energyBoxes[to] += amount;
System.out.printf(" 能量总和:%10.2f%n", getTotalEnergies());
//唤醒所有在lockObj对象上等待的线程
lockObj.notifyAll();
}
}
/**
* 获取能量世界的能量总和
*/
public double getTotalEnergies(){
double sum = 0;
for (double amount : energyBoxes)
sum += amount;
return sum;
}
/**
* 返回能量盒子的长度
*/
public int getBoxAmount(){
return energyBoxes.length;
}
}
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 = from;
this.maxAmount = max;
}
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();
}
}
}
public class EnergySystemTest {
//将要构建的能量世界中能量盒子数量
public static final int BOX_AMOUNT = 100;
//每个盒子初始能量
public static final double INITIAL_ENERGY = 1000;
public static void main(String[] args){
EnergySystem eng = new EnergySystem(BOX_AMOUNT, INITIAL_ENERGY);
for (int i = 0; i < BOX_AMOUNT; i++){
EnergyTransferTask task = new EnergyTransferTask(eng, i, INITIAL_ENERGY);
Thread t = new Thread(task,"TransferThread_"+i);
t.start();
}
}
}
争用条件:当多个线程同时共享同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现象称为争用条件。
012.png线程交互:互斥与同步
互斥:同一时间只能有一条线程对我们的关键数据或临界区进行操作。
同步:线程之间的通信机制,如某一线程做了一件事,用某种方式告诉其他的线程做完了。
同步实现:wait() / notify() / notifyAll()
如何扩展Java并发的知识
-
Java Memory Mode
- JMM描述了Java线程如何通过内存进行交互
- happens-before原则
- synchronized、volatile & final怎么实现这一原则
-
Lock & Condition
- Java锁机制和等待条件的高层实现
- java.util.concurrent.locks
-
线程安全性
- 原子性与可见性
- java.util.concurrent.atomic
- synchronized & volatile
- DeadLocks
-
多线程编程常用的交互模型
- Producer-Consumer模型
- Read-Write Lock模型
- Future模型
- Worker Thread模型
-
Java5中并发编程工具
- java.util.concurrent
- 线程池ExecutorService
- Callable & Future
- BlockingQueue
网友评论