如何创建并运行Java线程

Java线程类也是一个Object类,它的实例都继承自Java.lang.Thread 或者其子类。可以用如下方式在Java中创建一个线程

1
2
Thread thread = new Thread();
thread.start();// 执行该线程

编写线程运行的代码分为两种方式,一种是创建Thread子类的一个实例并且重写run()方法,第二种是创建类的时候实现Runnable 接口。

  1. 创建子类
1
2
3
4
5
6
7
8
9
10
//创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。
public class MyThread extends Thread
{
public void run()
{
System.out.println("running");
}
}
MyThread t1= new MyThread();
t1.start();

一旦线程启动后start()方法就会立即返回,而不会等待到run()方法执行完毕后才返回,就好像run()方法是在另外一个CPU上执行一样,当run方法执行后,将会打印出字符running。

  1. 实现runnable接口

runnable的中文意思即为任务,顾名思义,通过实现runnable接口,我们定义了一个子任务,然后将子任务交给Thread去执行。这种方式必须将runnable作为Thread类的参数。

新建一个实现了Java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。

1
2
3
4
5
6
7
public class MyRunnable implements Runnable
{
public void run()
{
System.out.println("running");
}
}

为了使线程可以执行run()方法,需要在Thread类的构造函数中传入MyRunnable 的实例对象

1
2
Thread thread =new Thread(new MyRunnable());
thread.start();// 当线程运行时,会调用实现了Runnable 接口的run方法。

Thread 类中有方法 getName 来得到当前线程的名字

但是实现runnable接口的子类并不是Thread的子类。所以没有getName方法。

通用做法:

1
String name =  Thread.currentThread().getName();

注意

1 因为线程操作的不确定性,所以主线程有可能最先执行完,那么此时其他线程不会受到任何影响,并不会随着主线程的结束而结束。 但是主线程一般还是最后完成,因为执行各种关闭动作。

2 一个Java程序启动至少会启动两个线程,每当使用Java命令执行一个类时,都会启动一个jvm,每一个jvm实际都是在OS中启动了一个线程,再加上垃圾收集线程,所以Java程序运行时,至少会启动两个线程,一个是主线程,一个是垃圾收集线程。

线程的状态

  • 创建(new)状态: 准备好了一个多线程的对象

  • 就绪(runnable)状态: 调用了start()方法, 等待CPU进行调度

  • 运行(running)状态: 执行run()方法

  • 阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用

  • 终止(dead)状态: 线程销毁

    创建一个进程后,并不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源等)只有线程运行的条件都满足了,才进入就绪状态。进入就绪状态也不代表获得了CPU的执行时间,因此只有等到获得了CPU的执行时间,线程才真正的进入运行状态。

线程类中的方法

静态方法

currentThread()

方法返回代码段正在被哪个线程调用的信息。

1
2
3
4
5
public class Run1
{
public static void main(String[] args)
{ System.out.println(Thread.currentThread.getName());}
}
sleep() 方法 线程的休眠

sleep()方法作用实在指定的毫秒数内让当前正在执行的线程休眠,这个正在执行的线程就是this.currentThread()返回的线程。

sleep 相当于让线程睡眠,交出CPU,让CPU执行其他任务,但是sleep()方法不会释放锁,如果当前线程持有某个对象的锁,即使调用sleep,其他线程也无法访问这个对象

如果调用了sleep方法,必须捕获InterruptedException 异常或者向上抛出。当线程睡眠时间满后,不一样会立即得到执行,因此此时可能CPU正在执行其他任务。所以相当于让线程阻塞

1
2
sleep(long millis) //毫秒
sleep(long millis,int nanoseconds) //毫秒 纳秒
yield()方法 线程的礼让

调用yield会让当前线程交出CPU权限,让CPU执行其他的线程,跟sleep一样,也不释放锁,但是yield不能控制具体的交出CPU的时间,另外,yield只能让相同优先级的线程获取CPU执行时间的机会。调用yield不会让线程阻塞,只会重新回到就绪状态。

对象方法

start()方法 start用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

run()方法 run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。

getId()方法获取线程的唯一标识

isAlive()方法 判断当前线程是否处在活动状态。

join()方法 很多情况下,主线程创建并启动了线程,如果子线程要进行大量的耗时运算,主线程往往早于子线程结束前结束,这时,如果主线程想等待子线程执行完成后再结束,就是对子线程用join方法,让他进行强制执行,作用就是等待线程对象销毁。用法为对象.join(),然后这个对象就先执行,执行完后再执行其他进程。

在线程操作中,可以使用join方法让一个线程强制执行,线程强制执行期间,其他线程无法运行,必须等待此线程完成以后才可以继续执行。

join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程,比如在线程b中调用了a的join方法,直到线程a执行完毕后,才会继续执行线程b.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Mythread implements Runnable
{
public void run()
{
for(int i=0;i<50;i++)
{
System.out.println(Thread.currentThread.getName()+i)
}
}
};

public class Demo
{
public static void main(String[] args)
{
Mythread mt = new Mythread();
Thread t =new Thread(t);
t.start();
for(int i=0;i<50;i++)
{
if(i>10)
{
t.join(); // exception
}
System.out.println(main运行 +i);
}
}
};
结果:
main 0
main 1
...
main 9
main 10
线程 0
线程 1
线程 2
...
线程 50
main 11
...

Object类方法

wait()方法导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。

notify()方法 选择一个在该对象上调用wait方法的线程,解除其等待状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。

任何对象都可以调用这两个方法,当在一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待,比如,在线程A中,调用了obj.wait()方法,那么线程A就会停止继续运行,而转为等待状态,等待到何时结束呢,线程A会一直等待到其他线程调用了obj.notify()方法为止

object.wait()方法并不是随时可以调用的,它必须包含在对应的Synchronized语句中,wait和notify都需要先获得目标对象的一个监视器.

Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?

答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。 wait()会释放锁,sleep不释放任何资源

停止线程

三种方式。

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用他们可能产生不可预料的结果。
  • 使用interrupt方法中断线程,但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

中断线程

线程中断不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出,至于目标线程接到通知后如何处理,则完全由目标线程自行决定.

当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。