1、概述
- 进程是程序(任务)的执行过程,并持有资源(共享内存、共享文件)和线程。
- 进程具有动态性。
- 进程是资源的载体,也是线程的载体。
- 资源指的是内存。
- 线程是系统中最小的执行单元。
- 同一进程中有多个线程。
- 线程共享进程的资源。
- 多个线程之间的通信为线程的交互。
- 线程交互的方式包括互斥与同步。
2、Java中线程的常用方法
- Java中实现线程的两种方法:
- 继承Thread类
- 实现Runnable接口
- 重写run方法
- Thread常用方法
![](https://img.haomeiwen.com/i3875336/ed7f5fc0285be7c0.png)
Thread常用方法.png
3、线程基本行为代码演示
package com.lord.demo.thead;
public class Actor extends Thread {
@Override
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 actress = new Thread(new Actress(), "Ms.Runnable");
actress.start();
}
}
class Actress implements Runnable {
@Override
public void run() {
// Thread.currentThread():获取当前线程的引用
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() + "的演出结束了!");
}
}
4、线程体验代码演示:(隋唐演义故事)
package com.lord.demo.concurrent;
/**
* 军队线程
* 模拟作战双方的行为
*/
public class ArmyRunnable implements Runnable {
// volatile很重要哦,这是可见性的关键
// volatile保证了线程可以正确的读取其他线程写入的值
// 可见性 ref JMM, happens-befor原则
volatile boolean keepRunning = true;
@Override
public void run() {
while (keepRunning) {
// 发动5连击
for (int i = 0; i < 5; i++) {
// Thread.currentThread():获取当前线程的引用
System.out.println(Thread.currentThread().getName() + "进攻对象[" + i + "]");
// 让出了处理器时间,下次该谁进攻还不一定呢!
Thread.yield();
}
}
System.out.println(Thread.currentThread().getName() + "结束了战斗!");
}
}
package com.lord.demo.concurrent;
/**
* 隋唐演义的大戏舞台
*/
public class Stage extends Thread {
@Override
public void run() {
System.out.println("欢迎观看隋唐演义");
try {
// 让观众们安静片刻,等待大戏上演
Thread.sleep(5000);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
System.out.println("大幕徐徐拉开...");
try {
Thread.sleep(5000);
} catch (InterruptedException e2) {
e2.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 e1) {
e1.printStackTrace();
}
/**
* 历史大戏留给关键人物
*/
mrCheng.start();
// 万众瞩目,所有线程等待程先生完成历史使命
try {
mrCheng.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生理想,为人民作出了贡献!");
System.out.println("谢谢观看隋唐演义,再见!");
}
public static void main(String[] args) {
new Stage().start();
}
}
package com.lord.demo.concurrent;
/**
* 关键人物
*/
public class KeyPersonThread extends Thread {
@Override
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() + "结束了战斗!");
}
}
5、Java线程的正确停止
- 千万、千万、千万不要使用stop方法。
- 使用退出标识(参考上述代码中的keepRunning)。
- interrupt方法初衷并不是用于停止线程。
6、线程交互
- 争用条件(Race Condition)当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据破坏(corrupted),这种现象称为争用条件。
- 互斥的实现:synchronized (intrinsic lock)。
- 同步的实现:wait()/notify()/notifyAll()。
7、线程交互代码演示:(宇宙的能量系统)
package com.lord.demo.racecondition;
/**
* 宇宙的能量系统
* 遵循能量守恒定律:
* 能量不会凭空创生或消失,只会从一处转移到另一处
*/
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;
}
}
package com.lord.demo.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 = from;
this.maxAmount = max;
}
@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.lord.demo.racecondition;
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();
}
}
}
8、Java线程提升参考
- Java Memory Mode
- JMM描述了Java线程如何通过内存进行交互
- happens-before
- synchronized、volatile & final
- Locks & 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
- 推荐书籍
- <java核心技术>-Java Volume I-Fundamentals
- <java并发编程实战>-Java Concurrency In Practice
网友评论