进程和 线程
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。线程可以理解为轻量级进程,是程序执行的最小单位。一个进程可以包含一个或者多个线程。
线程的生命周期如下所示:
新建线程
方式一:继承Thread类,然后重写里面的run()方法。然后new一个对象,调用start()方法。
Thread t1 = new Thread(){ @Override public void run(){ System.out.println("Hello world!"); } }; t1.start();
方式二:MyThread实现Runnable接口,然后将MyThread对象作为Thread的构造器入参,创建Thread对象,然后调用start()方法。
Thread t1 = new Thread(new Runnable(){ @Override public void run(){ System.out.println("Hello Runnable!"); } }); t1.start();
终止线程
通常来说,当我们启动一个线程后,都会让线程运行完成之后自动结束。可以通过Thread.stop()方法暴力终止线程,但是强烈不建议这样用。因为这样会造成程序的不可预测性,从而导致数据的不一致,而且不可恢复。
线程中断
线程中断是一种重要的线程协作机制。线程中断并不会使线程立即退出,而是给线程发送一个通知,告诉目标线程你可以退出了。其中主要包含以下3个API:
void Thread.interrupt();//中断线程 boolean Thread.isInterrupted;//判断是否被中断 static boolean Thread.interrupted();//判断是否被中断,并清除当前中断状态
当通知线程中断时,线程本身需要对中断做处理,否则对于线程的执行没有任何影响。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
if(Thread.currentThread().isInterrupted()) {
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000);
} catch (Exception e) {
System.out.println("Interrupted when sleep");
//设置中断状态
Thread.currentThread().interrupt();
}
Thread. yield ();
}
}
};
t1.setName("hugege");
t1.start();
Thread.sleep(2000);
t1.interrupt();
System.out.println("main end");
}
等待和通知
说到线程的等待和通知,对应的基础的两个方法就是wait()、notify()方法。这两个方法并不在Thread当中,而是在Object中。当一个线程调用了object.wait(),那么它就会进入到object对象的等待队列。当object.notify()方法被调用时,就会从这个等待队列中随机选一个线程进行唤醒操作。并且这个过程是完全随机的。
object还有一个notifyAll()方法,这个方法是唤醒所有的线程,然后让这些线程去竞争执行,获得监视器对象的才可以继续往下执行。这里面需要注意的是不论是wait()还是notify(),执行前都需要获取到当前对象的监视器,也就是获得当前对象的锁。下面给出一个demo:
/**
* wait()和notify()使用
*
*/public class SimpleWN {
static final Object object = new Object();
public static class T1 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T1 start!");
try {
System.out.println(System.currentTimeMillis()+"T1: wait for object ");
object.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+":T1 end!");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
object.notify();
System.out.println(System.currentTimeMillis()+"T2: end! ");
try {
Thread.sleep(2000);
} catch (Exception e) {
}
}
}
}
public static void main(String[] args) {
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
}
注意:Object.wait()和Thread.sleep()都可以让线程等待一段时间,Object.wait()是释放锁的,而Thread.sleep()是不释放锁的。
挂起和继续执行
这里面说线程挂起和继续执行分别指的是:suspend()和resume()方法。这里面挂起操作,是不释放锁资源的。查看JDK的文档会发现,这两个API已经被标记为过期了。所以在这里就不详细的介绍了。
等待线程结束和谦让
等待线程结束调用的方法是Thread.join(),JDK中提供了两个join方法:
final void join() throws InterruptedException; final synchronized void join(long millis) throws InterruptedException;
如调用了t1.join(),则当前线程会无限等待t1线程,直到t1执行完成。第二个方法则增加了一个超时时间,超过了时间之后,则继续往下执行。其中t1.join()可以理解为,让t1加入到当前线程的串行执行。附demo如下:
public class JoinMain { public volatile static int i = 0; public static class AddThread extends Thread { @Override public void run() { for(i = 0; i < 100000000; i++); } } public static void main(String[] args) throws InterruptedException { AddThread at = new AddThread(); at.start(); at.join(); System.out.println(i); } }
注意:t1.join()方法的本质是让主线程wait()在 t1 的对象实例上,当t1线程执行完成之后,会调用notifyAll(),从而唤醒主线程。所以在这里不要随意调用Thread.wait()和notify()方法,有可能扰乱join等方法的执行。
还有一个方法是Thread.yield(),这个方法是静态方法。Thread.yield()表示让当前线程让出CPU,然后再共同竞争CPU资源。这样只会增加别的线程执行的机会,而并不是当前线程一定不执行。