进程与线程
进程:计算机中具有某一功能的程序在数据集合上动态运行的过程,是系统进行资源分配的基本单位,数据资源是私有的。
线程:线程是轻量级的进程,是程序调度的最小单位,一个进程包含多个线程,进程的资源数据被线程所共享。
守护线程:守护线程是指在程序的运行过程中为非守护线程(用户线程)提供服务或者支持的线程,当用户线程全部结束后,就意味着守护线程失去了守护的对象,应用程序就结束了,虚拟机自然地退出。例如GC线程和JIT线程都属于守护线程。
线程的生命周期(状态图)
线程的创建我的另一篇https://blog.csdn.net/IPI715718/article/details/82429430。
这里强调一下run()方法和start()方法的区别
start()方法是线程的启动方法,一个线程在执行了此方法之后会进入就绪状态或者可执行状态,这时候线程还没有真正的执行,因为当该线程拿不到cpu资源等待,拿到cpu资源后会调用run()方法进入真正的运行状态。
run()方法是线程体方法,只有在start()方法中自动调用才会起到并行的作用,否则只是当前调用线程执行一个普通方法。
看一下源码
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);//向线程组中添加该线程
boolean started = false;
try {
start0();//调用本地方法执行run方法
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
线程的基本操作
线程的终止:stop()方法
@Deprecated
public final void stop() {}
当线程执行完此方法后,该线程会立即放下手中的工作,释放掉cpu资源,并且释放锁资源。这种方法太过于暴力,会强行将程序终止,很可能任务只进行到一半,造成数据的不一致性。
举例:long基本数据类型,64位每次只能写入32位,当第一次写入完成后,线程执行了stop方法,线程立即停止,会造成数据的不完整,读数据是得到的是一个错误的数据。
线程的中断:
线程的中断从字面理解为中断停止的意思,但是实际上自己有一套非常完善的线程退出机制,当线程执行interrupt()后并不会立即停止并且退出程序,而是给线程发一个通知告诉目标线程你需要退出了,至于什么时候停止全部交由目标线程决定,这样就避免了stop()方法的那种暴力行为。
public void interrupt() //中断线程
public boolean isInterrupted() //判断线程是否中断
public static boolean interrupted()//判断是否被中断,并清除中断状态
解释一下三种方法:interrupt方法中断线程,并设置中断标志位。isInterrupted根据中断标志位是否存在,返回该线程是否中断。interrupted根据中断标志位返回该线程是否中断,并且清除掉中断标志位。
以下代码会停止吗?答案不会,会永无休止的打印下去,原因是因为只有标志位,目标线程并没有去写什么时候退出线程的逻辑代码。
public class Test extends Thread {
@Override
public void run() {
int i = 0;
while(true) {
System.out.println(i++);
}
}
public static void main(String[] args) {
Test thread = new Test();
thread.start();
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
改进:这样程序会中断。
package test2;
public class Test extends Thread {
@Override
public void run() {
int i = 0;
while(true) {
//加入判断是否需要中断和中断推出的逻辑代码
if(isInterrupted()) {
break;
}
System.out.println(i++);
}
}
public static void main(String[] args) {
Test thread = new Test();
thread.start();
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
等待wait和通知notify/notifyAll
wait()方法和notify() /notifyAll()属于Object的方法,不属于Thread类的方法。Object是所有类的基类,也就意味着所有的对象都可以调用。这里注意:当某个对象调用wait方法时,必须获得当前对象的监视器。
在一个线程中当一个对象调用了wait()方法后,该线程将会释放掉锁和cpu资源进入该对象的等待队列中,当该对象执行notify方法后会从该对象的等待队列中随机唤醒一个等待线程,机会平等,人人有机会。当该对象调用notifyAll方法后会唤醒该对象的等待队列中的所有线程。唤醒后的线程进入就绪/可执行状态,只有重新获取cpu和锁资源才能继续执行。
解释一下这里的继续执行:继续执行的意思是当前线程拿到cup资源和锁资源后会执行wait方法之后的代码,并不是从头开始。
等待线程结束join和谦让yield
join方法
join方法是等待目标线程结束,在A线程中调用了B线程的join方法,A线程会进入等待,直到B线程执行结束后A线程才能继续执行。
看一下jdk源码
public final void join() throws InterruptedException {
join(0);
}
这里调用了下面millis=0。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
最终调用的是 public final synchronized void join(long millis){}方法,
会执行
if (millis == 0) {
while (isAlive()) {
wait(0);
}
}
wait(0)就是当前线程调用了wait()方法,使调用线程进入了当前线程对象的等待池中,当前线程执行结束后,当前线程对象会执行notifyAll方法,唤醒调用线程。
yield 方法
yield 方法是一个静态方法,一旦目标线程线程执行该方法,将会让出cpu资源,不会释放锁,让出资源后立马进入可运行状态,会立刻与其他线程再次进行资源的抢夺,也就意味着该目标线程不一定就不执行。