概念
一个在内存中运行的程序,都至少拥有一个进程,每个进程都拥有自己独立的一个内存空间(比如在Windows系统中,一个.exe,就是一个进程),进程是系统进行资源分配和调度的一个独立单位;而每个进程都至少拥有一个线程(默认就是主线程),进程的多个线程共享该进程的内存;线程是程序执行流的最小单位;
线程的状态
NEW
初始化状态,被声明创建,且没有调用start之前
READY
就绪状态,在调用start或者notify方法后进入该状态
RUNNING
运行状态,调用执行run方法后进入该状态
BLOCKED
阻塞状态,所谓的线程阻塞就是指线程因为某种原因放弃了CPU使用权,也即让出了CPU timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得CPU timeslice转到运行(running)状态。JVM因为管理的需要,把阻塞状态又分为BLOCKED,WAITING和TIMED_WAITING三个状态,把线程分别放入blocked和waiting两个队列里管理,如果别的线程运行出了synchronized这段代码,我只需要去blocked队列,放个出来。而某人调用了notify(),我只需要去waiting队列里取个出来。 不同的语义需要,一个是被BLOCKED,一个是主动WATING ,wait/notify可以实现更灵活的并发控制。
JVM实现堵塞有两种方式,一个是通过JVM的自旋(Spin-Waiting),另一个是使用操作系统中的挂起(Suspend)。在基于锁的算法中,如果一个线程在Spin或者Suspend的时候持有锁,那么其他线程都无法执行下去,也就是被阻塞了
DEAD
线程执行结束后
Java线程的两种方式
1、继承Thread类
实现方式:继承thread类,覆写thread类的run方法,调用start方法进行启动;
public class TestThread extends Thread {
@Override
public void run() {
}
public static void main(String args[]) {
TestThread thread = new TestThread();
thread.start();
}
}
缺点:java是单继承,一个类有一个父类,则无法继承Thread;
2、实现Runnable接口
实现方式:通过实现Runnable结果,覆写run方法,然后通过新建一个Thread来传入一个Runnable来启动线程;
public class TestRunnable implements Runnable {
@Override
public void run() {
}
public static void main(String args[]) {
TestRunnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
缺点:Runnable本身是一个接口,里面只有一个run方法,启动必须依赖Thread来进行启动;
一些常用方法
run方法
直接调用运行线程的run方法
start方法
通过调用该方法来开始执行线程,通过线程调度来运行run方法
stop方法
强制停止并结束该线程执行
join方法
调用该方法等待该线程结束
sleep方法
让该线程进入阻塞状态,但是不会释放对象锁
wait方法
让该线程进入阻塞状态,但是会释放对象锁
notify方法
与wait方法配合使用,唤醒正在阻塞状态下的线程
线程的同步方式
synchronized
是jvm的一种内置属性(内置锁),基于阻塞算法,适用于少量线程;jvm采用自旋(Spin-Waiting,将线程置于一个循环之中,每一次循环就去获取一次对象锁)的方式,让线程进行阻塞。
优点:实现简单,语义清晰,便于JVM堆栈跟踪;加锁解锁过程由JVM自动控制,提供了多种优化方案。
缺点:不能进行高级功能(定时,轮询和可中断等)。
Lock
一个是显示锁,主要集中使用Lock的一个子类ReentrantLock,基于阻塞算法,适用于大量线程;jvm采用挂起(Suspend,cpu把线程换进换出,当线程可使用的时候再运行,这种换进换出叫做上下文切换,需要cpu调度)的方式,让线程进行阻塞。
优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁
缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪。
原子变量类
原子变量类(Atomic),基于非阻塞算法,主要靠volatile和CAS操作实现的;当多个线程尝试使用CAS同时更新同一个变量时,只有一个线程能更新变量的值,而其它线程都将失败。然而,失败的线程并不会挂起(这与获取锁的情况不同:获取锁失败时,线程将被挂起),而是被告知在这次竞争中失败,并可以再次尝试。