进程与线程
进程是并发执行的程序在执行过程中分配和管理资源的基本单位。而进程是线程的容器,一个进程下可以有多个线程。
线程的生命周期
Thread的生命周期记录在内部的State中:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
1.新建线程与启动线程
Thread t = new Thread();
t.start();
2.终止线程
-
Thread.stop()
方法可以终止线程,但是已经被标注为废弃方法,因为该方法会直接释放线程持有的锁,可能会造成锁维护的对象的不一致状态。 - 安全的退出方法:可以在线程中监听一个状态值,需要退出时由线程自己按既定步骤退出。
3.线程中断
public void Thread.interrupted(); // 中断线程
public boolean Thread.isInterrupted(); // 判断线程是否被中断
public static boolean Thread.interrupted(); // 判断线程是否被中断,并清除当前中断状态
线程中断可以用来代替Thread.stop()
方法实现主动停止线程。线程中断并不会使线程立刻退出,而是向线程发送一个中断通知,目标线程接到通知之后如何处理,完全由目标线程自己决定。
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("quit");
break;
}
}
Thread.yield(); // 让出自己的时间片给其他线程
}
};
相对于之前的监听普通标记物的方法,中断命令由JDK提供支持,可以使Thread.sleep()
、Object.wait()
等方法执行时抛出InterruptedException
异常。因此更为泛用。(Thread.sleep()
在抛出异常之后会清除中断状态,如果需要在捕捉到中断异常之后检测中断状态,还需要重新手动设置中断状态)
public static native void sleep(long millis) throws InterruptedException;
public final void wait() throws InterruptedException
4.等待(wait)和通知(notify)
public final void wait() throws InterruptedException
public final native void notify();
public final native void notifyAll();
wait()
方法和notify()
方法属于java.lang.Object
对象。当在一个对象实例上调用wait()
方法之后,当前线程就会在这个对象上等待。
如果在线程A中调用了obj.wait()
方法,那么线程A就会停止继续执行,转为等待状态,直到其他线程调用了obj.notify()
方法为止。相当于obj对象成为了线程间通信的媒介。
当一个线程调用了obj.wait()
方法,那它就会进入obj对象的等待队列中,当obj.notify()
方法被其他线程调用时,会唤醒等待队列中的一个线程,这个唤醒是不公平、完全随机的。如果调用的是obj.notifyAll()
方法,则会唤醒等待队列中的所有线程。
想要使用obj.wait()
和obj.notify()
方法,必须先使用synchronized
获取obj对象,因为wait()
和notify()
方法执行前需要先获得obj对象的一个监视器,执行之后再释放obj的监视器给其他等待中的线程。
Thread.sleep()
、Object.wait()
方法都可以使线程等待若干时间,除了Object.wait()
方法可以被唤醒外,Object.wait()
方法还会释放obj的锁,而Thread.sleep()
不会释放任何资源。
5.挂起(suspend)和继续执行(resume)
Thread.suspend()
同样可以暂停线程,但是不会释放线程持有的资源,容易导致系统工作不正常,因此已被标为废弃。
6.等待线程结束(join)和谦让(yeild)
public final void join() throws InterruptedException // 无限制等待
public final synchronized void join(long millis) throws InterruptedException // 等待一段时间
当一个线程的执行依赖其他线程的执行结果时,就需要该线程等待目标线程执行结束后再继续执行。要做到这一点,可以使用wait()
方法,而Java提供的join()
方法同意可以做到。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread();
t.start();
t.join();
System.out.println("Hello World");
}
}
主线程调用t.join()
方法,等t线程结束执行之后再继续执行。join()
方法本质是让调用线程wait()
方法在当前对象实例上。JDK中join()
方法的实现核心片段如下:
while(isAlive()) {
wait(0);
}
Thread.yield()
方法可以使当前线程让出CPU资源,在这之后,该线程会立即加入之后的CPU资源争夺中,因此仍有可能继续执行。
public static native void yield();
Java关键字
1.volatile
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 禁止进行指令重排序。
因此,volatile关键字可以保证一个变量的可见性与一定程度上的有序性:
- 当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
- volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
volatile关键字并不能保证操作的原子性,如果一个变量进行的操作是非原子性的,如自增(++),那么即使它是volatile变量,这个操作也是非原子性的。
2.synchronized关键字
关键字synchronized的作用是实现线程之间的同步。它的工作是对同步的代码加锁,使得每一次,只能有一个线程进入同步块,从而保证线程的安全性。
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
- 直接作用于实例方法:相当于对当前实例对象加锁,进入同步代码前要获得当前实例对象的锁。
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
除了用于线程同步,确保线程安全外,关键字synchronized还可以保证线程之间的可见性和有序性。