您的位置:首页 > 编程语言 > Java开发

Java学习笔记―第十一章 多线程机制

2015-05-07 10:47 211 查看
第十一章 Java多线程机制了解Java中的进程与线程
1.1 进程:一般程序的结构大致可分为一个入口、一个出口和一个顺序执行的语句序列。程序运行时,系统从程序入口开始,按照语句的执行顺序(包括顺序、分支和循环)完成相应指令,然后从出口退出,程序结束。这样的结构称为进程。可以说,进程就是程序的一次动态执行的过程。一个进程既包括程序的代码,同时也包括系统的资源,如CPU、内存空间等。不同的进程所占用的系统资源都是独立的。
1.2 线程:线程是比进程更小的执行单位。一个进程在执行过程中,为了同时完成多个操作,可以产生多个线程。线程没有入口,也没有出口,不能自动运行,必须存在于某一进程中,由进程触发执行。在系统资源的使用上,属于同一进程的所有线程共享该进程的系统资源。

线程的生命周期
每个Java程序都有一个主线程。对于应用程序,主线程是main()方法执行的线索。要想实现多线程,必须在主线程中创建新的线程对象。新建的线程在一个完整的生命周期中通常需要经历创建、就绪、运行、阻塞、死亡五种状态。如下图所示:



2.1 新建状态:
当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。例如,下面的语句可以创建一个新的线程:
Thread myThread = new Thread();
2.2 就绪状态:
一个线程对象调用start()方法,即可使其处于就绪状态。处于就绪状态的线程具备了除CPU资源外的运行线程所需的所有资源。也就是说,就绪状态的线程排队等候CPU资源,而这将由系统进行调度。
2.3 运行状态:
处于就绪状态的线程获得CPU资源后即处于运行状态。每个Thread类及其子类的对象都有一个run()方法,当线程处于运行状态时,它将自动调用自身的run()方法,并开始执行run()方法中的内容。
2.4 阻塞状态:
处于运行状态的线程如果因为某种原因不能继续运行,则进入阻塞状态。阻塞状态与就绪状态的区别是:就绪状态只是因为缺少CPU资源不能执行,而阻塞状态可能会由于各种原因使得线程不能执行,而不仅是CPU资源。引起阻塞状态的原因解除后,线程再次转为就绪状态,等待分配CPU资源。
2.5 死亡状态:
当线程执行完run()方法的内容或被强制终止时,则处于死亡状态。至此,线程的生命周期结束。

掌握创建线程和启动线程的方法

3.1 创建线程
Java中创建线程有两种方式:一种是继承java.lang.Thread类,另一种是实现Runnable接口。
a.通过继承Thread类创建线程类
Java中定义了线程类Thread,用户可以通过继承Thread类,覆盖其run()方法创建线程类。语法格式 如下:
class <ClassName> extends Thread{
public void run(){
......//线程执行代码
}
}
b.通过实现Runnable接口创建线程类
class <ClassName> implements Runnable{
public void run(){
......//线程执行代码
}
}
3.2 线程的启动
线程创建完成后,通过线程的启动来运行线程。Thread类定义了start()方法用来完成线程的启动。
a. 继承Thread类线程的启动:创建完线程对象后调用start()方法即可。例如:
class MyThread extends Thread{ //继承Thread类创建线程类MyThread
public void run(){ //重写Thread类的run()方法
for(int i = 0; i<10; i++){
System.out.print(i+",")
}
}
}
public class ThreadExample{
public static void main(String[] args){
MyThread myThread = new MyThread(); //创建线程类MyThread的实例
myThread.start(); //启动线程
}
}
b.实现Runnable接口线程的启动:先基于此类创建对象,再将该对象作为Thread类构造方法 的参数,创建Thread类对象,最后通过Thread类对象调用start()方法启动线程。
class MyThread implements Runnable{ //实现Runnable接口创建线程类MyThread
public void run(){ //实现Runnable接口的run()方法
for(int i = 0; i<10; i++){
System.out.print(i+",")
}
}
}
public class ThreadExample{
public static void main(String[] args){
MyThread myThread = new MyThread(); //创建线程类MyThread的实例
Thread t = new Thread(myThread); //创建Thread类的实例

t.start(); //启动线程
}
}

4.线程的优先级
多线程中往往是多个线程同时在就绪队列中等待执行。优先级越高,越先执行;优先级越低,越晚执行;优先级相同时,遵循队列的“先进先出”原则。Thread类有三个与线程优先级相关的静态变量,分别是:
MIN_PRIORITY:线程能够具有的最小优先级(1)。
MAX_PRIORITY:线程能够具有的最大优先级(10)。
NORM_PRIORITY:线程的普通优先级,默认值为5。
当创建线程时,优先级默认为NORM_PRIORITY标识的整数5。可以通过setPriority()方法设置线程的优先级,也可以通过getPriority()方法获得线程的优先级。
Java的线程调度策略是基于优先级的抢占式的调度。优先级高的线程会抢占优先级低的线程的控制权。但是这种调度策略并非总是有效。
5. 线程的休眠
对于正在运行的线程,可以调用sleep()方法使其放弃CPU资源进行休眠,此线程转为阻塞状态。sleep()方法包含long型的参数,用于指定线程休眠的时间,单位为毫秒。sleep()方法会抛出非运行时异常InterruptedException,程序需要对此异常进行处理。
class MyThread extends Thread{
public void run(){
for(int i = 0; i<10; i++){
System.out.print(i+",");
try{
sleep(1000); //休眠1秒,即每隔1秒打印一个数字
}catch(InterruptedException e){
System.out.print("error:"+e);
}

}
}
}
public class ThreadExample{
public static void main(String[] args){
MyThread myThread = new MyThread();

myThread.start();

}
}
6. 线程让步
对于正在运行的线程,可以调用yield()方法使其重新在就绪队列中排队,并将CPU资源让给排在队列后面的线程。此时线程转为就绪状态。此外,yield()方法只让步给高优先级或同优先级的线程,如果就绪队列后面是低优先级线程,则继续执行此线程。yield()方法没有参数,也没有抛出异常。
class MyThread extends Thread{
public void run(){
for(int i=0; i<10; i++){
System.out.print(i);

yield();

}
}
}
public class ThreadExample{
public static void main(String[] args){
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();

myThread2.start();

}
}
7. 线程等待
对于正在执行的线程,可以调用join()方法等待其结束,然后才执行其他线程。join()方法有几种重载形式。其中,不带参数的join()方法表示等待线程执行结束为止。join()方法会抛出异常InterruptedException,程序需要对此异常进行处理。
class MyThread extends Thread{
public void run(){
for(int i=0; i<10; i++){
System.out.print(i);
}
}
}
public class ThreadExample{
public static void main(String[] args) throws InterruptedExceptio{
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread1.join(); //等待myThread1执行结束。
myThread2.start();
}
}
8. 多线程同步机制―同步方法的使用(synchronized)
在程序中运行多个线程时,可能会发生以下问题:当两个或多个线程同时访问一个变量,并且一个线程需要修改这个变量时,程序可能会出现预想不到的结果。要解决此类问题,需要使用synchronized关键字对共享资源进行加锁控制,进而实现线程的同步。synchronized关键字可以作为方法的修饰符,也可以修饰一个代码块。使用synchronized修饰的方法称为同步方法。当一个线程A执行这个同步方法时,试图调用这个同步方法的其他线程都必须等待,直到线程A退出该同步方法。
class MyThread implements Runnable{
private int count=0; //定义共享变量count
public void run(){
test();
}
private synchronized void test(){
for(int i=0; i<5; i++){
count++;
Thread.yield();
count--;
System.out.print(count+",");
}
}

}
public class ThreadExample{
public static void main(String[] args) throws InterruptedException{
MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread);
Thread t2 = new Thread(myThread);
t1.start();
t2.start();
}
}
当被synchronized修饰的方法执行完或发生异常时,会自动释放所加的锁。
当一个线程使用的同步方法用到某个变量,而此变量又需要其他线程修改后才能符合本线程的需要,那么可以在同步方法中使用wait()方法。使用wait()方法可以中断方法的执行,使本线程等待,暂时让出CPU资源的使用权,并允许其他线程使用这个同步方法。如果其他线程使用这个同步方法时不需要等待,那么它使用完这个同步方法时,应当使用notifyAll()方法通知所有由于使用这个同步方法二处于等待的线程结束等待。曾中断的线程会从中断处继续执行这个同步方法,并遵循“先中断先继续”的原则。如果使用notify()方法,那么只通知第一个处于等待的线程结束等待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: