摘自:《Java从入门到精通(第四版)》
Window操作系统是多任务操作系统,以进程为单位
进程:正在执行的程序;包含自身地址,独立执行的程序
系统给每个进程一段有限的使用CPU的时间,到下一个时间片时跳到另一个进程中,CPU快速转换,所以每个进程好像是同时执行一样
线程:进程中的执行流程,一个进程中可同时包含多个线程;若需要一个进程同时完成多段代码操作,就需要用到多线程,流程和上类似
线程的实现
继承 Thread
当 start() 调用一个已经启动的 ,会报 IllegalThreadStateException 异常
实质上,Thread类实现了Runnable接口,其中的 run() 正是对 Runnable 接口中 run() 的具体实现
class ThreadOne extends Thread {
private int count = 10;
@Override
public void run() {
while(true) {
System.out.println(count+"");
if(--count == 0) {
return;
}
}
}
}
ThreadOne thread1 = new ThreadOne();
thread1.start();
实现 Runnable 接口
Thread t = new Thread(new Runnable() {
@Override
public void run() {
int count = 10;
while(count > 0) {
System.out.println(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count-=1;
}
}
});
t.start();
生命周期
- 出生状态
- 就绪状态
- 运行状态
- 等待状态
- 休眠状态
- 阻塞状态
- 死亡状态
start() 之前出生状态,之后就绪状态(可执行状态)
得到系统资源,运行状态
运行状态 + wait() => 等待状态 + notify()/notifyAll() => 当前线程被唤醒/所有处于等待状态的线程被唤醒
运行状态 + sleep() => 休眠状态
运行状态 + 输入输出请求 => 阻塞状态 + 输入输出结束 => 就绪状态
run() 执行完毕 => 死亡状态
使线程处于就绪状态方法
- 调用sleep() <InterruptedException异常>
- 调用wait()
- 等待输入输出完成
就绪 => 运行
- 调用 notify() / notifyAll()
- 调用 interrupt()
- 线程结束休眠时间
- 输入输出结束
多线程
join:将A线程加入到B线程中,B线程会等A线程结束后再继续执行
threadA = new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while(true) {
// progressBar运行到 1% 停止,progressBar2开始运行直到结束,progressBar再开始运行
progressBar.setValue(++count);
try {
Thread.sleep(100);
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
threadA.start();
threadB = new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while(true) {
progressBar2.setValue(++count);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count == 100) {
break;
}
}
}
});
threadB.start();
现在的JDK中已经废除了stop(),提倡在run() 中无限循环,通过一个布尔型变量控制
interrupt():如果线程使用 sleep() 或 wait() 进入就绪状态,可通过 thread.interrupt() 使线程离开 run(),同时结束线程;会抛出 InterruptedException,可以在处理该异常时完成线程的中断业务处理,如终止while循环
yield():使运行状态的线程让出执行权,回到就绪状态,与其他同优先级线程一起再重新竞争执行权。对于支持多任务的操作系统来说,不需要调用该方法,因为操作系统会为线程自动分配CPU时间片来执行。
线程的优先级:决定首先让哪个线程进入运行状态,低优先级线程获得运行的几率比较小
Thread t1 = new Thread(new AThread());
Thread t2 = new Thread(new AThread());
Thread t3 = new Thread(new AThread());
t1.setPriority("t1",5,t1);
t2.setPriority("t2",4,t2);
t3.setPriority("t3",6,t3);
// 设置线程名称,优先级
public void setPriority(String threadName, int priority, Thread t) {
// 优先级需是1-10之内,否则报 IllegalArgumentException
t.setPriority(priority);
t.setName(threadName);
t.start();
}
线程同步
同步块
将共享组员的操作放置在 synchronized 定义的区域中,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域
public class ThreadSafeTest implements Runnable{
int num = 10;
@Override
public void run() {
while(true) {
synchronized ("") {
if(num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(--num);
}
}
}
}
public static void main(String[] args) {
ThreadSafeTest t = new ThreadSafeTest();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步方法
当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行
必须将每个能访问共享资源的方法修饰为 synchronized ,否则会出错
public class ThreadSafeTest implements Runnable{
int num = 10;
@Override
public void run() {
while(true) {
doit();
}
}
public synchronized void doit() {
if(num > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(--num);
}
}
public static void main(String[] args) {
ThreadSafeTest t = new ThreadSafeTest();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}