1.简单认识线程和进程
进程是指计算机上的一次执行活动,当运行一个程序时,就会启动一个进程。在多任务系统中,每个独立执行的程序称为一个进程。
线程是进程的一个实体,一个进程可以包含多个线程。和进程相比,线程占用的资源少,所以线程基本上可以认为是轻量级的进程。
2.为什么要使用线程
使用线程是为了最大限度的利用CPU资源,提高系统的效率,且看下面分析。
3.线程和进程的比较
- 进程是在操作系统上运行的,有独立的地址空间。进程之间的切换需要花费一定的时间和资源。而线程运行在进程内部,一个进程可以包含多个线程,线程之间的切换不会引起进程的切换。所以提高了系统的并发性能。
- 操作系统中,进程拥有独立的资源,互不干扰。所以进程之间的通信是很麻烦,很复杂的。线程运行在进程内部,多个线程可以共享这个进程的所有资源,所以多线程之间实现通信是比较容易的。
- 创建线程时不需要分配内存。它使用的是所属进程的资源。切换线程时,只需要保存少量的寄存器的内容。线程所消耗的系统资源远远小于进程消耗的资源。
4.线程的生命周期
线程生命周期看看这张经典的图就明白了。
1.新建状态 (New)
当一个线程对象被声明并创建时,线程就处于新建状态。例如: Thread t = new MyThread(),对象创建了,就处于了新建状态。在新建状态,线程并未分配到CPU资源,可以执行start()和stop()方法。
2.可运行状态(Runnable)
新建状态的的线程调用start()方法时,线程将进入线程队列等待CPU服务。该状态的线程位于可运行线程池中,等待获取CPU资源。(线程执行start()方法并不是直接就运行了,而是在线程池中等待获取CPU)
3.运行状态(Running)
在线程池中的线程获取到CPU资源以后,才可以执行,变成运行状态。Runnable状态是进入Running状态的唯一接口。也就是说线程想要进入运行状态,必须先进入可运行状态。
4.阻塞状态(Blocked)
正在执行的线程由于某种原因放弃了CPU的使用权,该线程就进入了阻塞状态。处于阻塞状态的线程不能立即进入可运行状态等待再次运行。只有阻塞的原因消失以后,才有可能再次获得CPU的使用权。
- 等待阻塞:线程运行wait()时,只有满足某种条件。此时,Java虚拟机才会把线程放入等待池中。
- 同步阻塞:如果线程获取锁失败时,Java虚拟机会把该线程放入锁池中。
- 其他阻塞:通过调用线程的sleep(),join()或发出I/O请求时,线程进入阻塞状态。当sleep()超时,join()等待线程完成或超时,I/O处理完毕的情况下。线程进入就绪状态。
5.线程结束状态(Dead)
线程执行完run()方法,或者调用stop()退出run()方法。或者抛出一个未捕获的异常时,该线程的生命周期就结束了。
5.实现多线程的方法
5.1 继承Thread类
继承Thread类必须重写run()方法,而且必须调用Thread类的start()方法启动线程。
package com.huawei.galaxy.thread;
public class ThreadTest extends Thread {
String threadName;
ThreadTest(String name) {
this.threadName = name;
}
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(threadName + " (" + i + ")");
}
}
public static void main(String[] args) {
new ThreadTest("Thread-1").start();
new ThreadTest("Thread-2").start();
new ThreadTest("Thread-3").start();
}
}
5.2 实现Runnable接口
实际上Thread类也实现了Runnable接口,Runnable接口提供了一个public void run()方法,使用Runnable接口也需要实现run()方法。也需要创建Thread对象,通过这个对象调用start(),最终调用run()方法来实现多线程。
使用Runnable接口实现创建多线程需要注意一下:
1)在创建类时实现Runnable接口,并重写run()方法。
2)由于Runnable接口,不能直接创建爱你所需类的对象并运行它,必须从Thread类的一个实例内部运行它。
package com.huawei.galaxy.thread;
public class RunnableTest implements Runnable{
String threadName;
RunnableTest(String name)
{
this.threadName = name;
}
@Override
public void run() {
for(int i = 0;i < 10;i++)
{
System.out.println(threadName + "(" + i + ")");
}
}
public static void main(String[] args) {
// 不能通过实现了Runnable接口的类创建线程,
// 实现了Runnable接口的线程必须运行在Thread对象内部。
Thread thread1 = new Thread(new RunnableTest("Thread-1"));
Thread thread2 = new Thread(new RunnableTest("Thread-2"));
Thread thread3 = new Thread(new RunnableTest("Thread-3"));
thread1.start();
thread2.start();
thread3.start();
}
}
最好还是用Runnable接口来实现线程。 因为Java是单继承的,使用Runnable接口可以避免单继承带来的局限。
6.线程的调度方法
线程调度方法有很多,自行查询API文档。主要讨论几个
6.1获取当前正在执行的线程
Thread类的静态方法,currentThread()方法可以获取当前正在执行的线程。
package com.huawei.galaxy.thread;
public class CurrentThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread());
}
public static void main(String[] args) {
Thread thread1 = new Thread(new CurrentThread());
Thread thread2 = new Thread(new CurrentThread());
thread1.start();
thread2.start();
}
}
执行结果:
Thread-0
|
6.2等待线程终止
join()方法可以暂停当前的线程,知道调用join方法的线程执行完成,在执行当前线程。
package com.huawei.galaxy.thread;
public class JoinTest implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out
.println(Thread.currentThread().getName() + "(" + i + ")");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args) {
Thread t0 = new Thread(new JoinTest());
Thread t1 = new Thread(new JoinTest());
System.out.println("0-" + t0.isAlive());
System.out.println("0-" + t1.isAlive());
t0.start();
t1.start();
System.out.println("1-" + t0.isAlive());
System.out.println("1-" + t1.isAlive());
try {
t0.join();
t1.join();
} catch (InterruptedException e) {
}
System.out.println("2-" + t0.isAlive());
System.out.println("2-" + t1.isAlive());
for(int i = 0; i< 5;i++)
{
System.out.println(Thread.currentThread().getName());
}
}
}
运行结果:
0-false
|
如果不写Join的运行结果如下:
0-false
|
6.3暂停正在执行的线程的方法
yield()方法会暂停当前正在执行的方法,让别的线程执行。比如线程A调用了yield()方法,作用就是线程A暂时不执行,让出CPU控制权,让线程B执行。
package com.huawei.galaxy.thread;
public class YieldThreadTest implements Runnable{
@Override
public void run() {
for(int i = 0;i<5;i++)
{
System.out.println(Thread.currentThread().getName() + " " + i);
Thread.yield();
}
}
public static void main(String[] args) {
YieldThreadTest yt = new YieldThreadTest();
Thread t1 = new Thread(yt);
Thread t2 = new Thread(yt);
Thread t3 = new Thread(yt);
t1.start();
t2.start();
t3.start();
}
}
执行结果:
Thread-0 0
|