java多线程知识详解

来源:互联网 发布:金枪鱼软件下载 编辑:程序博客网 时间:2024/06/10 05:41

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)

改变线程名称,使之与参数 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语句块:

 

 

 

 

 

 

 

用this作为synchronized块的参数传入synchronized块,作用域的当前对象。

(3)   wait()、notify()、notifyAll()(在同步方法中使用)

以上三个方法都是Object类中的final方法。

wait()方法可以中断方法的执行,使线程等待,让出CPU资源。并允许其他线程使用这个同步方法。

notify()方法通知处于等待状态的线程,使它结束等待。

notifyAll()方法通知所有的由于同步这个方法而处于等待的线程,结束他们的等待。

原创粉丝点击