java-day43
文章目录
Thread类中的构造器和核心方法
构造器:
//创建一个线程对象,默认的名字为Thread-0 1 2 3 4...
public Thread(){}
//创建一个线程对象,可以指定线程的名字
public Thread(String name){}
//创建一个线程对象,
Runnable实现对象中重写了run方法,run方 法中就是线程要
执行的代码
pub1ic Thread( Runnable target){}
//创建一个线程对象,指定Runnable实现类的同时还可以指定线程的名字
public Thread(Runnable target, String name){ }
核心方法:
//start方法可以启动一个线程对象,线程被启动后,会独立的自动运行run方法
//start方法中调用了start0方法来完成这个启动的工作。
public synchronized void start(){}
private native void start0();
//Thread类中的run方法是通过实行接口Runnable获取的。
//这个方法的作用就是指定当前线程的任务是什么
//所以都会重写run方法来指定线程要执行的任务。
public void run() {}
//把Thread类中的代码简化后:
public class Thread implements Runnable {
private Runnable target;
public Thread(Runnable target) {
this.target = target;
}
public Thread(){
}
public void run() {
if (target != null) {
target.run();
}
}
}
//Runnable接口的代码:
public interface Runnable {
public abstract void run();
}
线程状态
new:初始化状态
start():线程从初始变成就绪状态
Runnable:就绪状态
Running:运行状态
run():线程启动自动调用run方法
sleep():睡眠
join():连接
运行期间调用sleep、join方法,会进入阻塞状态
Otherwise Blocked:阻塞状态
sleep() time out:睡眠事件结束
join()
interupt()
阻塞状态下睡眠时间结束、连接结束或者被其他打断了就进入就绪状态
Dead:线程死亡状态 run方法运行结束
cpu时间片
每一个线程在使用cpu的时候,都只能使用很短的时间,因为不能让一个线程把cpu给独占了,cpu是所有程序代码共享的计算机资源,所以一个线程在使用一个很短的时间后就必须把cpu给交出去,让别的线程去使用。 那么操作系统为了方便管理和调度cpu的共享使用,就把cpu的使用时间划分成了一个个很短的时间段,这个就称为cpu的时间片。
默认情况,如果线程可以拿到一个cpu时间片的使用权,那么这个线程就可以在这个时间段中使用cpu来计算代码中要操作的数据。但是这个cpu时间片的一旦用完,那么线程就必须交出cpu的使用权,下一次另一个线程如果拿到一个cpu时间片的使用权,那么这个线程就可以使用cpu进行计算了。(将来这里还要考虑给代码加锁的问题)。
一般线程拿到cpu时间片的方式有俩种,一种是时间片轮换,另一种是时间片抢占。
时间片轮换,就是一个线程使用完,把cpu使用权交出去,然后下一个线程使用,一个个线程挨着来使用。
时间片抢占,所有要使用cpu的线程都可以抢夺cpu时间片的使用权,谁抢夺到了,那么谁就去使用cpu执行代码,时间片用完后交出cpu使用权,下一次的使用权,所有线程再重新抢夺。这种方式下,哪一个线程能拿到时间片的使用权,全靠概率。注意,默认环境下,都是采用的抢占式。
State
/**
* 尚未启动的线程的线程状态。(初始化状态)
*/
NEW,
/**
*可运行(就绪)线程的线程状态。可运行程序中的线程
*状态正在Java虚拟机中执行,但它可能
*正在等待来自操作系统的其他资源
*例如处理器。
*/
RUNNABLE,
/**
*等待监视器锁定时被阻止的线程的线程状态。
*处于阻塞状态的线程正在等待监视器锁定
*输入同步块/方法或
*调用后重新输入同步的块/方法
*{@link Object#wait()Object.wait}。
*/
BLOCKED,
/**
* 等待线程的线程状态。
* 由于调用
* 以下方法:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>处于等待状态的线程正在等待另一个线程
* 执行特定的动作。
*
*例如,调用了<tt>Object.wait()</tt>的线程
*一个对象正在等待另一个线程调用
*<tt>Object.notify()</tt>或<tt>Object.notifyAll()</tt>打开
*那个东西。调用了thread.join()的线程
*正在等待指定的线程终止。
*/
WAITING,
/**
* 具有指定等待时间的等待线程的线程状态。
* 由于调用
* 具有指定正等待时间的以下方法:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
*终止线程的线程状态(死亡状态)。
*线程已完成执行。
*/
TERMINATED;
t线程刚刚创建属于初始化状态
public class ThreadTest2 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
//...
}
};
System.out.println(t.getState());
}
}
main线程速度太快,还没来得及给t线程机会运行,处于就绪状态
public class ThreadTest2 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
//...
}
};
t.start();
System.out.println(t.getState());
}
}
run方法执行完,进入死亡状态
public class ThreadTest2 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
}
};
t.start();//启动t线程
//给t线程拖延时间
System.out.println("hello");
System.out.println(t.getState());
}
}
调用sleep处于有限期等待
public class ThreadTest2 {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();//启动t线程
//给t线程拖延时间
System.out.println("hello");
System.out.println(t.getState());
}
}
线程状态总结
在Thread类中,定义了一个枚举类型State,其中定义了线程的几种状态。
NEW 新建/初始化 状态
刚刚创建出的线程对象,还没有调用start方法的时候,属于这种状态。
特点:
线程没有存活
isAlive方法判断返回的是false
线程没有执行代码
线程不会抢夺CPU执行权
线程还没有被启动
RUNNABLE 可运行 状态
当线程对象调用start方法之后,就属于这种状态,这种状态包含俩种情况,第一种就是这个线程现在正在运行(is executing),那么它就属于RUNNABLE状态,第二种就是这个线程正在等待(be waiting)cpu的执行权,那么它也属于RUNNABLE状态。
特点:
线程存活
isAlive方法判断返回的是true
线程可以是正在运行代码
线程可以是没有在运行代码,但是可以抢夺CPU执行权
BLOCKED
WAITING
TIMED_WAITING "有限期"等待 状态
Thread类中的sleep(long)方法,Thread类中的join(long)方法被一个线程调用后,那么该线程就会进入到TIMED_WAITING状态。
特点:
线程存活
isAlive方法判断返回的是true
线程没有执行代码
线程不会抢夺CPU执行权
线程等指定的时间结束后会自动回到Runnable状态
TERMINATED 终止 状态
当一个线程把run方法全部执行完了,那么它就属于TERMINATED状态
特点:
线程死亡
isAlive方法判断返回的是false
线程没有执行代码
线程不会抢夺CPU执行权
线程不能重新启动,只有NEW状态下的线程才可以启动(start)
终止线程
线程运行完run方法后,会自动终止,并且被终止的线程就不能再使用。
如果线程的run方法还没有运行完,这时候是否可以终止线程?
Thread类有一个stop方法,可以终止线程,但是现在这个方法已经被标注为过时的方法,意思就是现在这个方法还在,但是不建议使用了,因为这个stop方法中存在安全隐患。
现在可以通过一个"标记变量"来控制线程结束
例如:
public class ThreadTest3 {
public static void main(String[] args) {
class MyThread extends Thread{
private boolean stop;
@Override
public void run() {
String name = Thread.currentThread().getName();
while(!stop) {
System.out.println(name+": hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+" 中的标记变量变为true,run方法即将结束运行,线程终止");
}
public void setStop(boolean stop) {
this.stop = stop;
}
}
MyThread t = new MyThread();
t.start();
try {
//main线程调用 睡眠5000
//睡醒后调用t线程中的方法改变标记变量,停止t线程的运行
Thread.sleep(5000);
t.setStop(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
isAlive方法,可以判断当前线程是否存活
Thread t = new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
System.out.println("hello");
System.out.println(t.getState());
System.out.println(t.isAlive());
//System.out.println(t.isAlive()+" - "+t.getState());
}
注意,线程是NEW和TERMINATED状态的时候,isAlive方法返回的是false,其他状态的情况都会是true
interrupt方法
该方法可以打断一个线程阻塞的状态。
interrupt方法中调用interrupt0方法,interrupt0是一个私有的本地方法:
private native void interrupt0();
interrupt0方法可以改变线程对象中的一个状态:interrupted status ,这状态就表示当前线程是不是被打断了,true表示被打断,false表示没有打断。
Thread中对interrupt0方法的表示为:Just to set the interrupt flag
sleep在进入阻塞状态之前,会先检查一个当前要阻塞的线程中的interrupted status这个状态值是不是false,如果是false就表示当前还没有其他线程想打断我们的阻塞状态,那么我们这个线程就可以正常进入到阻塞状态了,如果这个interrupted status状态值是true,那么就表示当前已经有其他线程要想打断我们的阻塞状态,那么这个sleep就会立马抛出异常来处于这个打断的请求,并且把interrupted status状态值从true改为false,因为这样下次在调用sleep方法的时候,就可以正常进入阻塞状态了。
同时,可以调用Thread类中的【非静态方法】isInterrupted来返回当前线程的interrupted status的状态值。这个值只有两种情况:true和false
true 表示当前【有】 线程要打断我们的阻塞状态
false表示当前【没有】线程要打断我们的阻塞状态
注意,这个非静态方法,只是返回这个“打断标识”值,并不会对这个值进行操作。
可以调用Thread类中的【静态方法】Thread.isInterrupted来返回当前线程的interrupted status的状态值。这个值只有两种情况:true和false
true 表示当前【有】 线程要打断我们的阻塞状态
false表示当前【没有】线程要打断我们的阻塞状态
注意,这个非静态方法,只是返回这个“打断标识”值,还会对这个值进行操作。如果是true,那么返回后,这个静态方法会把值修改为false。
此博客很详细:https://blog.youkuaiyun.com/zhuyong7/article/details/80852884
Thread中的三个方法:
//打断线程阻塞状态的方法,其实就是改变“打断标识”
public void interrupt(){}
//返回线程“打断标识”的值,但是不会清除这个值
public boolean isInterrupted(){
return isInterrupted(false);
}
//返回线程“打断标识”的值,但是[会]清除这 个值,把true改为false
public static boolean interrupted(){
return currentThread().isInterrupted(true);
}
join方法
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
join对于线程的改变
package com.zzb.day44;
public class ThreadTest3 {
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1执行完,即将死亡");
}
};
Thread t2 = new Thread() {
public void run() {
//t2要等待t1的死亡
try {
t1.join();
System.out.println("t1死亡,t2执行");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("t2执行完,即将死亡");
}
};
t1.start();
t2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t1.getState());
System.out.println(t2.getState());
}
}