进程是程序的一次执行过程,是系统运行程序的基本单位。线程与进程类型,但线程是一个比进程更小的执行单位。一个进程在执行的过程中可以产生多个线程。同类的多个线程共享同一块内存空间和一组系统资源,所以系统产生一个线程或者在各个线程之间进行切换时,负担要比进程小很多,正因如此,线程也被称为轻量级进程。
1.线程的状态
线程在生命周期中可能会处于以下6种状态
线程创建之后会处于新建状态,调用start()方法后先进入就绪状态,在线程获得CPU时间片后就处于运行状态。当线程调用同步方法时,在没有获取到锁的情况下,会进入到阻塞状态。当线程执行wait()方法后,进入等待状态,等待状态的线程当有其他线程通知时才会回到运行状态。而超时等待状态相当于在等待状态的基础上增加了超时限制,比如通过sleep(long millis)或wait(long millis)方法可以使线程进入超时等待状态。当超时等待的时间到达后,线程会回到运行状态。在线程执行完运行状态的run()方法后会进入到终止状态。
2.如何创建线程
线程的创建可以通过继承Thread类来实现
public class MyThread extends Thread{
@Override
public void run(){
}
}
class test{
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
}
或者通过实现Runnable接口的方式来实现
public class MyThread implements Runnable{
@Override
public void run(){
}
}
class test{
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread td = new Thread(mt);
td.start();
}
}
实现Runnable接口的方式可以避免继承Thread类的方式由于Java单继承特性带来的缺陷,Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况
3.线程死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程是被无期限地阻塞,所以程序不可能正常地结束。
比如线程A拥有资源2,线程B拥有资源1,它们同时都想申请对方的资源,所以两个线程就会等待而进入死锁的状态。
如果想避免死锁,可以在占有部分资源的线程进一步申请其他资源时,如果申请不到,就主动释放它所占有的资源。或者可以按顺序来申请资源,要释放资源则按相反的顺序进行释放,破坏循环等待的条件。
4.sleep()方法和wait()方法的区别
- 两者都可以暂停线程的执行
- sleep方法没有释放锁,而wait方法释放了锁
- wait方法通常用于线程间的通信,sleep方法通常用于线程的暂停执行
- wait方法被调用后,线程不会自动苏醒,需要其他线程调用同一个对象的notify()方法,sleep方法执行完后,线程会自动苏醒
5.线程池
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,这就是”池化资源”技术产生的原因。先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中。线程池的思想主要是为了,从而减少创建和销毁线程对象的开销,提高资源利用率。
使用线程池的好处:
- 提高响应速度,当任务到达时,可以不需要等待线程创建就可以立即执行
- 降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控