进程与线程
进程:程序(任务)的执行过程(动态的),持有资源(共享内存,共享文件)和线程
如在我们电脑上的QQ,如果只是放在那里,并不是进程,只有当你点击它运行后,才启动了一个进程。
线程:如QQ,可以文字聊天,同时首发文件,这就是2个线程。
线程是系统中最小的执行单元,同一个进程可以拥有多个线程,线程共享进程的资源。
线程的交互:互斥(竞争)与同步(合作)
如,在同一个班级中,每个同学都是一个线程,大家共享公共资源如桌椅,黑板,学习资料等。同学间可以交流,关系有互斥与同步。如当同学A与同学B同时向使用某个学习资料时,就产生了互斥,需要一个一个使用;当学校组织文艺汇演时,需要同学间互相合作,才能表演优秀的节目。
Java线程之初体验
- Java对线程的支持
- Thread类与Runnable接口,都有run()方法
- 线程的创建和启动
- 线程常用方法
以隋唐英雄演绎这个实战项目来进一步了解Java的多线程
- 根据故事梗概,我们需要“军队-ArmyRunnable”,“英雄人物-KeyPersonThread”,“舞台-Stage”这三个类;
-
/**
* 军队线程
* 模拟作战双方的行为
* @author
*
*/
public class ArmyRunnable implements Runnable {
//volatile(可见性 JMM, happens-before原则)保证线程可以正确读取其他线程写入的值
volatile boolean keepRunning = true;
@Override
public void run() {
// TODO Auto-generated method stub
while(keepRunning) {
//发动5连击
for(int i=0; i<5; i++) {
System.out.println(Thread.currentThread().getName() +
"进攻对方第[" + i + "]击");
//让出了CPU时间,下次该谁进攻还不一定呢!
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() + "结束了战斗!");
}
}
/**
* 隋唐演义的大戏舞台
* @author
*
*/
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) {
// TODO Auto-generated catch block
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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//历史大戏留给关键人物
mrCheng.start();
try {
mrCheng.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生梦想,为人民作出了贡献");
System.out.println("谢谢观看隋唐演义,谢谢!");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Stage().start();
}
} - 如何正确的停止线程
- stop()方法就是一个不正确的停止线程的方法;它会使线程戛然而止,我们不知道它完成了什么工作,没有完成什么工作,因此是错误的。
- 使用退出旗标:如上面的例子中keepRunning就是一个结束标志。
- interrupt()方法:初衷并不是用于停止线程
Java线程交互
- 消失的能量
- 什么是争用条件(Race Condition)
- 当多个线程同时访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据破坏(corrupted),这种现象称为争用条件。
- 如上图所示,当某个时间片时,线程1获得了CPU时间,从主存中读取了energyBox[to]的值,然后加上500,但还没有写回内存时,CPU时间耗尽;然后线程2抢到了CPU,读取的energyBox[to]的值仍为5000,然后它操作加上900,并写回内存5900,然后CPU时间耗尽。而下面又是线程1获得了时间片,将它上面计算完的5500写入内存,导致最终内存中存储的值为5500.而通过线程1和线程2的修改,最终写入内存的energyBox[to]的值应该为6400才是正确的!经过这样的方式运行,就发生了能量不守恒的结果。
- 那么,如何修改代码,使能量变得守恒呢?
- 互斥与同步,得到线程正确的进行交互。
- 互斥:关键数据在同一时间只能被一个线程访问。实现:synchronized(intrinsic lock)
- 同步:由于资源某些条件不满足,导致所有的线程陷入等待状态;而当访问资源的条件满足后,会唤醒所有的线程,进入互斥状态。实现:wait()/notify()/notifyAll()——都是Object对象的方法。
- wait()/notify()/notifyAll()不是在同一个线程的同一次操作中执行的。因为同步是指多个线程之间的同步,只有一个线程的话是不需要同步的!!
- 当一个线程要访问一个临界区(Critical Section)时,首先要获得该临界区的锁,当获得了锁后去判断是否满足获得该资源的条件,当条件不满足时,将释放锁(使得其他线程可以进入临界区),并进入该锁对象上的等待(wait())队列中等待。当其它线程使用完了临界区后,将告知(notifyAll())所有等待队列中的线程:目前临界区内的条件有变化,大家可以开始竞争锁对象。
- 什么是争用条件(Race Condition)
要点回顾
- 如何创建线程及线程的基本操作;
- 可见性及volatile关键字;
- 争用条件;
- 线程的互斥synchronized;
- 线程的同步wait/notifyAll();
扩展
- 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
- 线程池ExcecutorService
- Callable & Future
- BlockingQueue