JAVA线程简介
Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。线程可以是守护线程或非守护线程。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且默认情况下,当且仅当创建它的线程是守护线程时,新线程才是守护程序。当 Java 虚拟机启动时,通常都
·
Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。线程可以是守护线程或非守护线程。
当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,
并且默认情况下,当且仅当创建它的线程是守护线程时,新线程才是守护程序。
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。
Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:
* 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
* 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常 。
守护线程:它是指只要程序还在运行,它就应该在后台提供某种公共服务的线程,但它不属于程序的核心部分 。
例如:main的线程就属于非守护线程。若要创建守护线程,我们在它启动之前,调用setDaemon()方法来设置。
关于守护线程更多参考《守护线程》
创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
然后,下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143);
p.start();
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。
然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
然后,下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。
主要方法:
public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
抛出:
IllegalThreadStateException - 如果线程已经启动。
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
注意:我觉得他不一定能执行其他线程。它的本质应该是让系统再次来进行线程的调度。
那么应该也有可能该线程被调度(当然这点可能要依赖JVM的实现)。
public static void sleep(long millis)
throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
该线程不丢失任何监视器的所属权。
参数:
millis - 以毫秒为单位的休眠时间。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除。
注意1: sleep不会释放对象的锁,而wait则会 。
注意2 :sleep可以被interrupt()方法中断而被唤醒。具体参考《线程的interrupt》
public final void setPriority(int newPriority)
更改线程的优先级。
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。
参数:
newPriority - 要为线程设定的优先级
抛出:
IllegalArgumentException - 如果优先级不在 MIN_PRIORITY 到 MAX_PRIORITY 范围内。
SecurityException - 如果当前线程无法修改该线程。
注意1:我们可以通过调用 setPriority()方法来设置线程的优先级,线程的优先级总共有10级,
但为了保证可移植性,我们最好使用MIN_PRIORITY、 NORM_PRIORITY和MAX_PRIORITY。
public final void join()
throws InterruptedException
当前等待该线程终止后才继续运行。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除。
注意:一个线程可以调用另一个线程的join()方法,主叫线程会先挂起,而等到那个线程结束后再继续运行。
我们也可以给join()方法给一个 timeout参数,这样如果目标线程在期限到期后还没有结束,
join()就会强制返回。而且join()方法还会被主叫线程的interrupt() 方法打断。
其使用可参见实例1
interrupt
public void interrupt()
中断线程。
如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。
如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。
如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。
如果以前的条件都没有保存,则该线程的中断状态将被设置。
中断一个不处于活动状态的线程不需要任何作用。
抛出:
SecurityException - 如果当前线程无法修改该线程
注意:关于interrupt()的更多内容请参考《线程interrupt》
已经过时的方法
public final void stop()
已过时。 该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器
(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,
则损坏的对象将对其他线程可见,这有可能导致任意的行为。
stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。
目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。
如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。
有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。
强迫线程停止执行。
无论该线程在做些什么,它所代表的线程都被迫异常停止,并抛出一个新创建的 ThreadDeath 对象,作为异常。
停止一个尚未启动的线程是允许的。如果最后启动了该线程,它会立即终止。
应用程序通常不应试图捕获 ThreadDeath,除非它必须执行某些异常的清除操作
(注意,抛出 ThreadDeath 将导致 try 语句的 finally 子句在线程正式终止前执行)。
如果 catch 子句捕获了一个 ThreadDeath 对象,则重新抛出该对象很重要,因为这样该线程才会真正终止。
注意:该方法已经过时。还是用变量来控制run()函数的结束,从而控制线程的介绍比较好。
public void destroy()
已过时。 该方法最初用于破坏该线程,但不作任何清除。它所保持的任何监视器都会保持锁定状态。
不过,该方法决不会被实现。即使要实现,它也极有可能以 suspend() 方式被死锁。
如果目标线程被破坏时保持一个保护关键系统资源的锁,则任何线程在任何时候都无法再次访问该资源。
如果另一个线程曾试图锁定该资源,则会出现死锁。这类死锁通常会证明它们自己是“冻结”的进程。
抛出:
NoSuchMethodError - 始终
注意1:该方法已经过时。
注意2:该方法决不会被实现(没实现就抛异常NoSuchMethodError)。即使要实现,它也极有可能以 suspend() 方式被死锁
public final void suspend()
已过时。 该方法已经遭到反对,因为它具有固有的死锁倾向。
如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。
如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。
有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。
挂起线程。
首先,调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
如果线程处于活动状态则被挂起,且不再有进一步的活动,除非重新开始。
抛出:
SecurityException - 如果当前线程不能修改该线程。
另请参见:
checkAccess()
注意1:该方法已经过时。
注意2:它具有固有的死锁倾向。线程使用suspend()挂起时,仍然保持有锁
public final void resume()
已过时。 该方法只与 suspend() 一起使用,但 suspend() 已经遭到反对,因为它具有死锁倾向。
有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。
重新开始挂起的进程。
首先,调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
如果线程处于活动状态但被挂起,则它会在执行过程中重新开始并允许继续活动。
抛出:
SecurityException - 如果当前线程不能修改该线程。
注意:该方法已经过时。
共享资源:
semaphore:用于线程间通信的标志对象,我们可以通过使用synchronized关键字作为函数定义的前缀来保护代码。
原子操作:在成员变量前面加上volatile关键字。
关键段:有时只需要防止多个线程同时访问方法中的某一部分,我们可以使用synchronized关键段。如下:
synchronized(syncObject) {
? // This code can be accessed
? // by only one thread at a time
}
线程的状态,有如下四种 :
New :已经创建完毕,但还没有开始启动。
Runnable :只要cpu分给其时间,就能运行。
Dead :中止线程的正确的方法是退出run()方法。
Blocked :就本身而言,是可运行的,但因为某种原因而被阻塞。除非重新进入Runnable否则, cpu不会分给其时间 。
关于synchronized,wait(),notify()和notifyall()方法请参阅《线程中的wait和notify方法》
实例1:
join的使用
static void JoinDemo()
{
int n=3;
Thread threads[]=new Thread[n];
for(int i=0;i<n;i++)
{
if(i==0)
threads =new Thread(new JoinDemoRunnable());
else
threads =new Thread(new JoinDemoRunnable(threads[i-1]));
}
for(int i=0;i<n;i++)
{
threads .start();
}
}
class JoinDemoRunnable implements Runnable
{
long id=0;
static long count=0;
Thread t;
JoinDemoRunnable()
{
this(null);
}
JoinDemoRunnable(Thread t)
{
id=count++;
this.t=t;
}
boolean blRun=true;
public void run()
{ int cnt=0;
while(blRun)
{
if(t!=null)
{
try
{ t.join();
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
System.out.println(id+"run"+cnt);
cnt++;
if(cnt==2)
{
blRun=false;
}
}
System.out.println("thread"+id+" exit");
}
}
结果 :
0run0
0run1
thread0 exit
1run0
1run1
thread1 exit
2run0
2run1
thread2 exit
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。线程可以是守护线程或非守护线程。
当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,
并且默认情况下,当且仅当创建它的线程是守护线程时,新线程才是守护程序。
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。
* 调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
* 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常
例如:main的线程就属于非守护线程。若要创建守护线程,我们在它启动之前,调用setDaemon()方法来设置。
关于守护线程更多参考《守护线程》
创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
然后,下列代码会创建并启动一个线程:
PrimeThread p = new PrimeThread(143);
p.start();
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。
然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
然后,下列代码会创建并启动一个线程:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。
主要方法:
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
抛出:
IllegalThreadStateException - 如果线程已经启动。
暂停当前正在执行的线程对象,并执行其他线程。
注意:我觉得他不一定能执行其他线程。它的本质应该是让系统再次来进行线程的调度。
那么应该也有可能该线程被调度(当然这点可能要依赖JVM的实现)。
public static void sleep(long millis)
throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
该线程不丢失任何监视器的所属权。
参数:
millis - 以毫秒为单位的休眠时间。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除。
注意1:
注意2
public final void setPriority(int newPriority)
更改线程的优先级。
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。
参数:
newPriority - 要为线程设定的优先级
抛出:
IllegalArgumentException - 如果优先级不在 MIN_PRIORITY 到 MAX_PRIORITY 范围内。
SecurityException - 如果当前线程无法修改该线程。
注意1:我们可以通过调用 setPriority()方法来设置线程的优先级,线程的优先级总共有10级,
但为了保证可移植性,我们最好使用MIN_PRIORITY、 NORM_PRIORITY和MAX_PRIORITY。
public final void join()
throws InterruptedException
当前等待该线程终止后才继续运行。
抛出:
InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除。
注意:一个线程可以调用另一个线程的join()方法,主叫线程会先挂起,而等到那个线程结束后再继续运行。
我们也可以给join()方法给一个 timeout参数,这样如果目标线程在期限到期后还没有结束,
join()就会强制返回。而且join()方法还会被主叫线程的interrupt() 方法打断。
其使用可参见实例1
interrupt
中断线程。
如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。
如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。
如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。
如果以前的条件都没有保存,则该线程的中断状态将被设置。
中断一个不处于活动状态的线程不需要任何作用。
抛出:
SecurityException - 如果当前线程无法修改该线程
注意:关于interrupt()的更多内容请参考《线程interrupt》
已过时。 该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器
(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,
则损坏的对象将对其他线程可见,这有可能导致任意的行为。
stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。
目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。
如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。
有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。
强迫线程停止执行。
无论该线程在做些什么,它所代表的线程都被迫异常停止,并抛出一个新创建的 ThreadDeath 对象,作为异常。
停止一个尚未启动的线程是允许的。如果最后启动了该线程,它会立即终止。
应用程序通常不应试图捕获 ThreadDeath,除非它必须执行某些异常的清除操作
(注意,抛出 ThreadDeath 将导致 try 语句的 finally 子句在线程正式终止前执行)。
如果 catch 子句捕获了一个 ThreadDeath 对象,则重新抛出该对象很重要,因为这样该线程才会真正终止。
注意:该方法已经过时。还是用变量来控制run()函数的结束,从而控制线程的介绍比较好。
已过时。 该方法最初用于破坏该线程,但不作任何清除。它所保持的任何监视器都会保持锁定状态。
不过,该方法决不会被实现。即使要实现,它也极有可能以 suspend() 方式被死锁。
如果目标线程被破坏时保持一个保护关键系统资源的锁,则任何线程在任何时候都无法再次访问该资源。
如果另一个线程曾试图锁定该资源,则会出现死锁。这类死锁通常会证明它们自己是“冻结”的进程。
抛出:
NoSuchMethodError - 始终
注意1:该方法已经过时。
注意2:该方法决不会被实现(没实现就抛异常NoSuchMethodError)。即使要实现,它也极有可能以 suspend() 方式被死锁
public final void suspend()
已过时。 该方法已经遭到反对,因为它具有固有的死锁倾向。
如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。
如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。
有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。
挂起线程。
首先,调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
如果线程处于活动状态则被挂起,且不再有进一步的活动,除非重新开始。
抛出:
SecurityException - 如果当前线程不能修改该线程。
另请参见:
checkAccess()
注意1:该方法已经过时。
注意2:它具有固有的死锁倾向。线程使用suspend()挂起时,仍然保持有锁
已过时。 该方法只与 suspend() 一起使用,但 suspend() 已经遭到反对,因为它具有死锁倾向。
有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。
重新开始挂起的进程。
首先,调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。
如果线程处于活动状态但被挂起,则它会在执行过程中重新开始并允许继续活动。
抛出:
SecurityException - 如果当前线程不能修改该线程。
注意:该方法已经过时。
共享资源:
semaphore:用于线程间通信的标志对象,我们可以通过使用synchronized关键字作为函数定义的前缀来保护代码。
原子操作:在成员变量前面加上volatile关键字。
关键段:有时只需要防止多个线程同时访问方法中的某一部分,我们可以使用synchronized关键段。如下:
synchronized(syncObject) {
? // This code can be accessed
? // by only one thread at a time
}
实例1:
static void JoinDemo()
{
int n=3;
Thread threads[]=new Thread[n];
for(int i=0;i<n;i++)
{
if(i==0)
threads
else
threads
}
for(int i=0;i<n;i++)
{
threads
}
}
class JoinDemoRunnable implements Runnable
{
long id=0;
static long count=0;
Thread t;
JoinDemoRunnable()
{
this(null);
}
JoinDemoRunnable(Thread t)
{
id=count++;
this.t=t;
}
boolean blRun=true;
public void run()
{ int cnt=0;
while(blRun)
{
if(t!=null)
{
try
{ t.join();
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
System.out.println(id+"run"+cnt);
cnt++;
if(cnt==2)
{
blRun=false;
}
}
System.out.println("thread"+id+" exit");
}
}
0run0
0run1
thread0 exit
1run0
1run1
thread1 exit
2run0
2run1
thread2 exit
更多推荐
已为社区贡献18条内容
所有评论(0)