1. 概念:
程序:一段静态的代码,它是应用软件执行的蓝本
进程:程序的一次动态执行过程,即程序从代码加载、执行至执行完毕的一个完整过程。
线程:比进程更小的执行单位。每个程序都有一个默认的主线程。
2. 线程的状态与生命周期
使用Thread类及其子类的对象来表示线程,新建线程的完整的生命周期要经历4种状态。
(1) 新建
当一个Thread类或子类的对象被声明并创建时,新的线程对象处于新建状态。
创建新执行线程有两种方法。
1. 将类声明为 Thread
的子类。该子类应重写 Thread
类的 run
方法。接下来可以分配并启动该子类的实例。
2. 声明实现 Runnable
接口的类。该类然后实现 run
方法。然后可以分配该类的实例,在创建 Thread
时作为一个参数来传递并启动。
(2) 运行
线程在创建之后就具备了运行的能力。
线程创建后仅仅是占有了内存资源,在JVM中还没有这个线程,因此,
此线程必须调用start()方法通知JVM。如果线程是Thread的子类创建的,该类中run()会立即执行,因此子类必须重写run()方法。
(3) 中断
4种原因中断:
1. 切换。JVM将CPU资源从当前线程切换到其他线程,使本线程让出CPU的使用权处于中断状态。
2. 休眠。线程使用CPU资源期间,如果执行了sleep(int millsecond)方法,使当前线程进入休眠状态,参数millsecond指定毫秒数之后,该线程重新进到队列中排队等候CPU资源。
3. 等待。线程使用CPU资源期间,如果执行了wait()方法,使得当前线程进入等待状态。等待线程不会主动进到线程队列排队,其他线程必须调用notify()方法通知它。
4. 阻塞。线程使用CPU资源期间,执行某个操作进入阻塞状态。,只有消除阻塞原因线程才能进入线程队列排队。
(4) 死亡
处于死亡状态下的线程不再具有运行的能力。死亡状态:线程释放了实体,即释放分配给线程对象的内存。
2种死亡原因:
1. 正常运行的线程完成了它所有的工作(执行完run()方法)。
2. 线程被提前强制性终止(强制run()方法结束)。
3. 线程的常用方法
返回值类型 | 常用方法 | 说明 |
void | start() | 启动线程。线程对象调用该方法。run()方法没有结束之前不得调用start()方法,否则发生IllegalThreadStateException异常 |
void | run() | 定义线程对象被调用之后所需要执行的操作 |
void | sleep(int millsecond) | 休眠。线程的调用执行是按照其优先级的高低顺序进行的。有时高优先级线程需要低优先级线程做一些工作,这时高优先级线程可调用sleep(int millsecond)方法使其休眠参数millsecond指定毫秒。如果线程在休眠时被打断,会抛出InterruptedException异常 |
boolean | isAlive() | 测试线程是否处于活动状态。当前线程处于“新建”或者“死亡”状态后,线程仍可以调用isAlive()方法,返回flase,如果线程的run()方法结束之前调用isAlive()方法,即没有进入“死亡”,返回true |
static Thread | currentThread() | currentThread()方法是Thread类中的类方法,可以用类名调用,返回正在使用CPU资源的线程。 |
void | interrupt() | “吵醒”休眠的程序。当一些线程调用sleep()方法处于休眠状态时,一个占有CPU资源的线程可以让休眠的线程调用该方法“吵醒”自己。即导致休眠线程发生InterruptedException异常 |
String | getName() | 返回该线程的名称 |
void | setName(String name) | 改变线程名称,使之与参数 |
void | setDaemon(boolean n) | 将线程设置成一个守护线程 |
void | setPriority(int grade) | 线程调用该方法调整线程优先级 |
int | getPriority() | 返回线程的优先级 |
Thread.State | getState() | 返回线程的状态 |
void | join() | 等待该线程终止 |
void | yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
4. 用Thread的子类创建线程
创建步骤:
(1) 定义一个类继承Thread类。——定义类
(2) 覆盖Thread类中run()。——重写run()方法
(3) 直接创建Thread的子类对象。——创建线程
(4) 调用start()方法。——启动线程
例:xcRun.c
public class xcRunextends Thread{ //定义xcRun类继承Thread类
xcRun(Strings){ //构造方法设置线程名称
setName(s);
}
public void run(){ //重写父类Thread的run方法,封装
for(int i=0;i<4;i++){
System.out.println("我是:"+getName()+",我运行了");
try{
sleep(2000); //休眠2000毫秒
}
catch(InterruptedExceptione){ }
} } }
test.c
public class test{
public static voidmain(String[] args) {
xcRunxc1 = new xcRun("线程一");//直接创建Thread的子类对象
xcRunxc2 = new xcRun("线程二");
xc1.start(); //开启线程一
xc2.start(); //开启线程二
}
}
运行结果: 我是:线程一,我运行了
我是:线程二,我运行了
我是:线程一,我运行了
我是:线程二,我运行了
我是:线程一,我运行了
我是:线程二,我运行了
我是:线程一,我运行了
我是:线程二,我运行了
分析:因为CPU是随机切换线程,所有每次结果都可能不同
5. 使用Runnable接口创建线程
创建步骤:
(1) 定义一个类实现Runnable接口。——定义类
(2) 覆盖Runnable接口中的run()方法。——重写run()方法
(3) 通过Thread类创建线程对象。——Thread创建对象
(4) 将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数——向Thread类传递子类对象
(5) 调用Thread类的start()方法开启线程,并调用Runnable接口子类的run()方法。——开启线程
====*====*====*====*====*====*====*====*====*====*====*====*=====*=====*==
例:xcRun.c
public class xcRun implementsRunnable{
public void run(){//重写Rnuuable接口中run()方法
Stringname = Thread.currentThread().getName();//将当前线程的名字赋给name
for(inti=0;i<4;i++){
System.out.println("我是:"+name+",我运行了");
try{
Thread.sleep(2000); //休眠
}
catch(InterruptedExceptione){ }
} } }
test.c
public class test {
public static void main(String[]args) {
xcRunxc = new xcRun(); //创建Runnable接口的子类对象
Threadxc1 = new Thread(xc);//将Runnable接口子类对象传递给Thread类的构造函数
Threadxc2 = new Thread(xc);
xc1.setName("线程一"); //为线程取名字
xc2.setName("线程二");
xc1.start(); //开启线程
xc2.start();
}
}
运行结果: 我是:线程一,我运行了
我是:线程二,我运行了
我是:线程一,我运行了
我是:线程二,我运行了
我是:线程一,我运行了
我是:线程二,我运行了
我是:线程二,我运行了
我是:线程一,我运行了
分析:因为CPU是随机切换线程,所有每次结果都可能不同
总结:两种进程创建方式比较
Aextends Thread:
l 简单
l 不能再继承其他类了(Java单继承)
l 同份资源不共享
Aimplements Runnable:(推荐)
l 多个线程共享一个目标资源,适合多线程处理同一份资源。
l 该类还可以继承其他类,也可以实现其他接口。
6. 控制线程(线程的联合)
join()方法:调用join()方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。
有人也把这种方式称为联合线程:一个线程A在占有CPU资源期间,可以让其他线程调用join()方法和本线程联合。B.join() 称A运行期间联合了B,A立即中断执行,等待B执行完毕,A重新排队,恢复执行。如果联合前A已经结束,则B.join()没有任何效果。
join()方法的重载方法:
join(long millis):
join(long millis,int nanos):
7. 线程的同步
(1) synchronized关键字:
在处理线程同步时,第一步就是把方法用关键字synchronized进行修饰,当线程A使用这个方法时,其他线程想使用这个方法就必须等待,直到线程A使用完该方法。(线程不会同时访问synchronized修饰的方法)
例:publicsynchronized void nethod(){…….},
该方法同一时刻只能被一个线程访问。
注意:
² synchronized关键字不能继承。 ² 定义接口时不能使用synchronized关键字 ² 构造方法不能使用synchronized关键字 ² synchronized关键字只能用来同步方法,不能用来同步类变量
|
(2) synchronized块;
synchronized关键字还可以用于方法中某个块中,表示只对这个块的资源互斥访问。
public void method(){ …… synchronized(表达式){ …… } } |
synchronized语句块:
l 用this作为synchronized块的参数传入synchronized块,作用域的当前对象。
(3) wait()、notify()、notifyAll()(在同步方法中使用)
以上三个方法都是Object类中的final方法。
l wait()方法可以中断方法的执行,使线程等待,让出CPU资源。并允许其他线程使用这个同步方法。
l notify()方法通知处于等待状态的线程,使它结束等待。
l notifyAll()方法通知所有的由于同步这个方法而处于等待的线程,结束他们的等待。
如果需要java开发视频教程的加QQ群:814394029,可联系群主获取,非诚勿扰哦!