51、线程(Thread)
(
程序 program 是对数据描述与操作的代码的集合,是应用程序执行的脚本
进程 process 程序一次执行过程,系统运行程序的基本单位。程序是静态的,进程是动态的
系统运行一个程序即是一个进程从创建、运行到消亡的进程。
多任务 multi task 在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程
线程 thread 单位比进程 process 更小,程序中单个顺序的流控制,一个进程中可以包含多个线程。
线程是一个独立的执行流,是进程内部的一个独立执行单元,相当于一个子程序
)
实现多线程的两种方式(java程序启动时,jvm自动创建主线程)
1、(继承Thread 类)创建java.lang.Thread类的子类,重写该类的 run 方法
重写 run( ) 方法,将业务代码写在方法体中
2、(实现 Runnable 接口)创建java.lang.Runnabl-e接口的实现类,实现接口中的 run 方法
定义的线程类已经显示继承了其它类,就不能再继承Thread 类了。
适合用于资源共享
Runnable 接口必须实现 run( ) 方法,而Thread 类中的run( ) 方法是一个空方法,可以不重写
Runnable 接口的实现类并不是真正的线程类,只是线程运行的目标类,要想以线程的方式执行 run 方法必须依靠Thread 类
启动线程
1、创建Thread 对象,调用Thread 对象的start( ) 方法,不是调用run( ) 方法
2、创建Thread 对象,传入参数 Thread(Runnable target),并调用该Thread 对象的start( ) 方法
资源共享(不考虑线程安全问题)
实例需求:使用Thread 类,创建两个线程,共同打印0-99
实例代码1:(继承Thread 类)
public class PrintNumber {
public static void main(String[] args) {
int i = 0;
NumberThread thread1 = new NumberThread("Thread1");
NumberThread thread2 = new NumberThread("Thread2");
thread1.setI(i);
thread1.start();
thread2.start();
}
}
class NumberThread extends Thread{
public NumberThread(String threadName) {
super(threadName);
}
@Override
public void run() {
for( ;i < 100 ;i++){
System.out.println(getName()+" : " + i);
}
}
private static int i;
public static void setI(int i) {
NumberThread.i = i;
}
}
实例代码2:(实现Runnable 接口)
public class MyRunnable implements Runnable {
int i = 0;
public void run() {
for(; i<=100 ;i++){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread thread1 = new Thread(mr);
Thread thread2 = new Thread(mr);
thread1.start();
thread2.start();
}
线程的生命周期【状态】(方法:yield( ) 、sleep( ) 、join( )、Interrupt( )、isAlive( ))
新建:创建一个Thread对象时,该对象就处于 新建状态
可执行:等待调度,不是马上执行
执行:处于可执行的状态的线程对象,一旦获得了CPU控制权,就转换为执行状态
调用 yield( ) 方法,当前线程让出CPU控制权,并重新回到可执行状态,等待调度
阻塞:线程在执行状态下由于某种条件的影响被迫让出CPU控制权
调用 sleep( ) 方法,当前线程进入堵塞状态,暂时休眠一段时间,参数单位为毫秒
调用 join( ) 方法 , 合并某个线程,处在执行状态的线程如果有其他线程调用join( )方法,当前线程将被挂起进入阻塞状态
目标线程执行完毕后才会解除阻塞,回到可执行状态
执行I/O操作 , 线程在执行过程中访问外部资源而堵塞
interrupt( ) 方法:解除处于阻塞状态的线程的阻塞状态, 抛出异常 InterruptedException
消亡:处于执行状态的线程一旦从run方法返回,则进入死亡状态,死亡状态的线程不能被重新运行。
isAlive( ) 方法,用于判断该线程是否死亡
已经结束的线程,不能被重新执行,否则会抛 IllegalThreadStateException
线程调度
按照特定机制为线程分配 CPU 时间片段的行为
Java 程序运行时,由Java 虚拟机负责线程的调度
线程调度的实现方式
分时调度模型
让所有线程轮流获得CPU的控制权,并且为每个线程平均分配CPU时间片段
抢占式调度模式(Java 虚拟机采用此种调度模型)
选择优先级相对较高的线程执行,如果所有线程的优先级相同,则随机选择一个线程执行,
线程优先级(不推荐使用 )
Thread 类提供设置和获取线程优先级的方法
getPriority( ) 方法:获取当前线程的优先级
setPriority( ) 方法:设置当前线程的优先级
Java语言为线程类设置了10个优先级,分别使用1~10内的整数表示 ,整数值越大代表优先级越高。
每个线程都有一个默认的优先级,主线程的默认优先级是5
线程同步
问题:线程并发,多个线程共享一个资源,出现线程安全问题
线程安全:多线程应用程序同时访问共享对象时,由于线程间相互抢占CPU的控制权
造成一个线程夹在另一个线程的执行过程中运行,所以可能导致错误的执行结果。
解决线程安全问题(Synchronized 关键字)
使用synchronized 代码块:需要在synchronized 代码块中参照共同的一个对象,否则不行。
实例代码:(拿苹果)
public class ShareApple implements Runnable {
private int appleCount = 5;
boolean getApple(){
synchronized (this) {
if(appleCount > 0 ){
appleCount--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿走一个苹果"+",还剩下" + appleCount + "个苹果");
return true;
}
return false;
}
}
public void run() {
boolean flag = getApple();
while(flag){
flag = getApple();
}
System.out.println(Thread.currentThread().getName() + "线程结束了");
}
public static void main(String[] args) {
ShareApple shareApple = new ShareApple();
Thread th1 = new Thread(shareApple);
Thread th2 = new Thread(shareApple);
th1.setName("EngineerZhong");
th2.setName("EngineerYe");
th1.start();
th2.start();
}
}
线程通信(wait( )、notify( )、notifyall( ))
实例:三个人排队买票,一张票5元,售票处有一张5元
EngineerZhong 20 元(排队等待其余两位买好后,才够找零)
EngineerYe 5元
EngineerZhou 5元
实例代码:
public class ThreadByeTicket implements Runnable {
private int fiveCount = 1,tenCount = 0,twentyCount = 0;
public synchronized void byeTicket(){
String name = Thread.currentThread().getName();
if("EngineerZhong".equals(name)){
if(fiveCount < 3){
System.out.println("5元面值的钱有:" + fiveCount + "张 EngineerZhong等待买票~~");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("5元面值的钱有:" + fiveCount + "张 卖给EngineerZhong一张票,找零15~~");
}
}else if("EngineerYe".equals(name)){
fiveCount++;
System.out.println("5元面值的钱有:" + fiveCount + "张 卖给EngineerYe一张票,钱正好~~");
}else if("EngineerZhou".equals(name)){
fiveCount++;
System.out.println("5元面值的钱有:" + fiveCount + "张 卖给EngineerZhou一张票,钱正好~~");
}
if(fiveCount == 3){
notifyAll();
}
}
public void run() {
byeTicket();
}
public static void main(String[] args) {
ThreadByeTicket ticketDemo = new ThreadByeTicket();
Thread th1 = new Thread(ticketDemo);
th1.setName("EngineerZhong");
Thread th2 = new Thread(ticketDemo);
th2.setName("EngineerYe");
Thread th3 = new Thread(ticketDemo);
th3.setName("EngineerZhou");
th1.start();
th2.start();
th3.start();
}
}
更新时间:2016年10月7日