进程是操作系统资源分配的最小单位,一个进程里的多个线程共享该进程的所有资源。
线程是CPU调度的最小单位,必须依赖进程而存在。
线程并行指多个线程同时工作,是基于多核CPU实现的。而并发是一段时间内的并发量,并发是依赖时间区间存在的。
线程启动方式
根据jdk官方自述,线程归结为两种启动方式,一是创建Thread子类,第二种是实现Runable接口。
线程中止
不建议使用stop()强行终止线程,容易导致线程执行到一半就被中止执行了,过于霸道。
推荐使用interrupt()来改变中断状态为true,告诉线程可以考虑停止了,我们甚至都不用关心最终这个线程是否会停止。这种方式会更加“柔和”,符合Java给线程的定义:线程是协作式的,而不是抢占式
。
interrupt()方法仅仅改变线程的中断状态,不实际中断线程的运行。
调用interrupt()后,会将线程的中断标志设置为true,这时候程序可以通过while(t.isInterrupt()){}控制程序的运行。如果程序不对当前线程中断状态做判断,interrupt()并不会中断线程。所以处于死锁中的线程无法被中断
。
对比下面三段代码,发现捕捉线程中断的异常后,会重置中断状态
这样带来的好处是,可以在catch(InterruptedException e){… …}中释放当前线程占有的其他资源后再次确认中断。
//线程中断后,中断状态为true,并且t线程结束运行
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());//false
}
}
});
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
System.out.println("调用t.isInterrupted()后,当前线程的中断状态为:"+ t.isInterrupted());//true
}
//线程中断后,中断状态为false,并且t线程不会结束。因为捕捉异常后,重置了线程中断状态,所以总是while(true)
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());//false
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
System.out.println("调用t.isInterrupted()后,当前线程的中断状态为:"+ t.isInterrupted());//false
}
//线程中断后,中断状态为false,并且t线程结束运行。因此在catch()中再次调用了中断线程方法
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());
try{
Thread.currentThread().sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();//如果不在catch中调用interrupt(),线程不会中止,因为抛出中断异常时,程序重置了中断状态
System.out.println("当前线程的中断状态为:"+ Thread.currentThread().isInterrupted());//true
}
}
}
});
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
System.out.println("调用t.isInterrupted()后,当前线程的中断状态为:"+ t.isInterrupted());//false
}
interrupted()和isInterrupted()方法异同
同:两个方法都可以获取当前线程的中断状态,返回boolean值
异:interrupted()是一个静态方法,isInterrupted()是一个普通方法。当调用interrupted()后,会重置中断状态为false,而调用isInterrupted()后,中断状态不变。
可以对一个线程对象调用两次start()方法吗?
不行,会抛出线程状态异常。
public static void main(String[] args){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
t.start();//抛出异常IllegalThreadStateException。表示该线程已经启动过了
}
线程状态转换
可以看到,sleep()中的线程处于阻塞状态,这时候调用interrupt(),线程重新回到就绪状态。这也说明了interrupt()可以打断sleep()中的线程。
思考一下,如果一个线程正在运行状态,调用了interrupt()方法,该线程会处于什么状态?
答案是仍然是运行状态。因为interrupt()并不实际中断线程,而是改变中断状态,剩下的交给程序处理。
其他线程相关方法
yeild():让出当前线程的CPU执行权,但不会释放线程的锁资源。执行yeild()方法的线程可能在让出CPU执行权后又马上重新获得CPU执行权。
join():可以将两个线程有顺序的执行。比如在B线程中调用的A线程的join(),B线程会等A线程执行完后再继续执行。当我们的多线程负责某些模块任务的时候,如果模块之间在某些情况下存在先后顺序,join()非常好用。
守护线程
Daemon(守护)线程是一种支持型线程,当虚拟机的所有非守护线程都中止了,守护线程会自动退出。JVM的垃圾回收线程就是一个守护线程。注意,在守护线程的run()方法内定义的finally不一定会执行。
//最后打印在控制台的语句有可能是"我是守护线程",也可能是"finally"。意味着finally块内容不一定执行
//其余情况下的finally块内容会正常执行
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){
try{
System.out.println("我是守护线程");
}finally {
System.out.println("finally");
}
}
}
});
t.setDaemon(true);
t.start();
Thread.sleep(100);
}