线程调度模型
1分时调度模型: 系统平均分配CPU时间片,所有线程轮流占用CPU.
2抢占式调度模型: 系统按照线程优先级来分配CPU时间片,优先级高的线程获取CPU执行时间相对多一些.
线程的优先级
Thread类里的这个属性private int priority代表线程的优先级.优先级值的范围为1-10.
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
获取线程优先级的方法
public final int getPriority() {
return priority;
}
设置线程优先级的方法
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
这个set方法和我们写的实体类有点些许不一样呀.因为线程的优先级取值是1-10,进行了传参的校验.还进行了线程组优先级的校验.checkAccess()这个方法是对安全方面的校验.虽然很繁琐,但是我还是觉着思想是好的.有约束才能成方圆嘛.
了解到这里,java其实线程模型采用的也是抢占式调度.空说无凭,难以服众.上案例.
public class PriorityThreadDemo {
static class PriorityThread extends Thread {
static int threadName = 1;
//线程的名称.
public PriorityThread() {
super("thread-" + threadName);
threadName++;
}
//线程的执行次数.
long threadExecuteNum = 0;
@Override
public void run() {
//死循环,看线程执行的次数,
for (int i = 0; ; i++) {
threadExecuteNum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
List<PriorityThread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
PriorityThread priorityThread = new PriorityThread();
priorityThread.setPriority(i + 1);
threads.add(priorityThread);
}
for (PriorityThread thread : threads) {
thread.start();
}
Thread.sleep(1000);
for (PriorityThread thread : threads) {
thread.stop();
}
for (PriorityThread thread : threads) {
System.out.println("线程优先级" + thread.getPriority() + "线程获取时间片" + thread.threadExecuteNum);
}
}
}
结果我就不贴出来.可以直接复制代码跑起来,就能看到结果.不过有一点还是要说明,优先级高并不代表一定就会百分百高于其他线程,就像抢占式解释的那样,只是概率高.
线程的生命周期
Thread类里的属性private volatile threadStatusint = 0;代表线程的状态.
NEW状态:创建成功但是还没有调用start方法.
RUNNABLE状态:调用了start方法以后,处于一个就绪状态或者执行状态.(就绪状态就是还未获得CPU执行权,运行态就是获得了CPU执行权)
TERMINATED状态: run方法执行完成以后变成终止状态,当线程执行过程中发生了异常没有被捕获也会进入终止状态.
TIMED_WAITING状态:线程进入限时等待状态.
处于限时等待状态的情况.
Thread.sleep(long n);使得线程进入限时等待,时间为n毫秒.
Object .wait();带时限的抢占对象monitor锁.
Thread.join();带时限的合并线程.
LockSupport.parkNanos();让线程等待时间为纳秒.
LockSupport.parkUntil();让线程等待时间可以灵活设置.
public class StatueThread {
static List<Thread> threadList = new ArrayList<>();
//线程名称.
static int threadNum = 0;
private static void printThreadStatues() {
for (Thread threadDemo : threadList) {
System.out.println("线程" + threadDemo.getName() + "的状态为" + threadDemo.getState());
}
}
private static void addThreadStatues(Thread thread) {
threadList.add(thread);
}
static class StatuesThreadDemo extends Thread {
public StatuesThreadDemo() {
super("线程--" + (++threadNum));
//将自己加入集合中.
threadList.add(this);
}
@Override
public void run() {
System.out.println("线程--" + this.getName() + "的状态为" + this.getState());
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadStatues();
}
System.out.println("线程--" + this.getName() + "运行结束");
}
}
public static void main(String[] args) throws InterruptedException {
addThreadStatues(Thread.currentThread());
StatuesThreadDemo threadDemo1 = new StatuesThreadDemo();
System.out.println(threadDemo1.getName() + "--状态为--" + threadDemo1.getState());
StatuesThreadDemo threadDemo2 = new StatuesThreadDemo();
System.out.println(threadDemo2.getName() + "--状态为--" + threadDemo1.getState());
StatuesThreadDemo threadDemo3 = new StatuesThreadDemo();
System.out.println(threadDemo3.getName() + "--状态为--" + threadDemo1.getState());
threadDemo1.start();
Thread.sleep(5000);
threadDemo2.start();
Thread.sleep(5000);
threadDemo3.start();
Thread.sleep(1000);
}
}
线程的基本操作
1线程名称在启动之前设置.但也允许为运行的线程设置名称.
2允许两个线程的名字相同,但应该避免.
3如果没有指定线程的名称,系统会自动设置线程的名称.
public class ThreadDemo {
static class ThreadNameTarget implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行的轮次" + i);
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadNameTarget threadNameTarget = new ThreadNameTarget();
new Thread(threadNameTarget).start();
new Thread(threadNameTarget).start();
new Thread(threadNameTarget, "线程--A").start();
new Thread(threadNameTarget, "线程--A").start();
Thread.sleep(Integer.MAX_VALUE);
}
}
线程的sleep操作
睡眠我的作用是让当前线程休眠,把cpu的执行权让给其他线程.线程状态从运行态变成阻塞状态.
Thread类中sleep方法是一组重载的静态方法.会有InterruptedException受检异常.
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException
具体的例子就不往出贴了.小伙伴们可以自行尝试体验下.
线程的interrupted操作.
java当中已经提供了stop()方法作为线程的停止操作,但是被设置为过时的操作.是因为无法知道当前线程处于什么状态,有可能持有某把锁,或者正在进行数据库操作,导致属于一致性问题等.才不建议使用stop()方法.
interrupted()方法的作用,如果线程处于阻塞状态(如果调用了Object.wait()方法),就会立马退出阻塞.并且抛出InterruptedException异常,线程可以捕获并且进行处理.Thread.join()和Thread.sleep()方法也会有同样的情况.
如果线程处于运行状态,线程就不会受任何影响,继续运行,只是线程中断标记被设置为true.在适当的位置调用isInterrupted()方法查看自己是否被中断,并执行退出操作.可以看出线程被打断抛出异常,停止执行后面的逻辑.(执行结果我就不贴出来了,小伙伴们可以自行复制代码体会,虽然案例很简单,但是思考的过程真的很重要,不仅仅是你们,我自己也是.)
public class InterruptedDemo {
static int threadNum = 1;
static class SleepThread extends Thread {
//构造方法.
public SleepThread() {
super("sleepThread-" + threadNum);
threadNum++;
}
@Override
public void run() {
System.out.println(getName() + "进入了睡眠");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(getName() + "发生异常被打断");
return;
}
System.out.println(getName() + "线程执行结束了.");
}
}
public static void main(String[] args) throws InterruptedException {
SleepThread thread1 = new SleepThread();
thread1.start();
SleepThread thread2 = new SleepThread();
thread2.start();
//主线程睡眠两秒.
Thread.sleep(2000);
//打断线程1.
thread1.interrupt();
//主线程等待五秒.
Thread.sleep(50000);
thread2.interrupt();
Thread.sleep(1000);
System.out.println("程序结束");
}
}
可以看出线程被打断抛出异常,停止执行后面的逻辑.(执行结果我就不贴出来了,小伙伴们可以自行复制代码体会,虽然案例很简单,但是思考的过程真的很重要,不仅仅是你们,我自己也是.)
public class RunThread {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程开始执行了");
while (true) {
System.out.println(Thread.currentThread().isInterrupted());
if (Thread.currentThread().isInterrupted()) {
System.out.println("线程结束了");
return;
}
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
Thread.sleep(2000);
thread.interrupt();
}
}
虽然run方法里是一个无条件不会停止的循环,但是通过标志位,还是会停止这个线程.
线程的join操作
线程join操作有三个版本.
public final void join() throws InterruptedException {
}
此方法会把当前线程变为WAITING状态.直到线程合并结束.
public final synchronized void join(long millis)
throws InterruptedException {}
此方法会把当前线程变为TIME_WAITING状态,直到合并线程结束,或者被合并线程执行millis的时间.
public final synchronized void join(long millis, int nanos)
throws InterruptedException {}
此方法会把当前线程变为TIME_WAITING状态,直到合并线程结束,或者被合并线程执行millis+nanos的时间.
join方法的要点.
1: join方法是一个实例方法,需要使用被合并线程的句柄(或者指针 变量)去调用,比如thread.join().
执行这段代码的当前线程为合并线程,会进入TIME_WAITING等待状态,让出CPU执行权,等待被合并线程执行完毕.
2: 如果设置了被合并线程的执行时间mills(或者mills+nanos),并不能保证当前线程一定会回复RUNNABLE状态.(java是抢占式调度,这样会好理解为什么).
3: 如果主动方合并线程在等待时被中断,就会抛出InterruptedException 受检异常.(受检异常和运行时异常可以研究研究,深入了解下)
说了这么多,感觉合并点还是有点模糊,合并点其实可以理解成就是A线程等待B线程执行完在开始的地方.
public class JoinThread {
static int threadNum = 1;
static class SleepThread extends Thread {
//构造方法.
public SleepThread() {
super("sleepThread-" + threadNum);
threadNum++;
}
@Override
public void run() {
System.out.println(getName() + "进入了睡眠");
System.out.println(getName() + "线程执行结束了.");
}
}
public static void main(String[] args) {
SleepThread sleepThread1 = new SleepThread();
System.out.println("线程启动.");
sleepThread1.start();
try {
//主线程合并sleepThread1
sleepThread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
SleepThread sleepThread2 = new SleepThread();
sleepThread2.start();
try {
//主线程合并sleepThread2
sleepThread2.join(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程结束.");
}
}
从这个例子中就能看出来,等线程一和线程二执行完毕以后,主线程才会打印线程结束,而每个join方法就是合并点.
线程的yield操作.
线程的yield(让步)操作就是把当前线程持有的CPU执行权让出来,让其他线程去持有CPU,但是线程状态依旧是RUNNABLE状态,对应的操作系统就是从执行态转变为就绪态,所以从这里可以想象到,它刚让出CPU执行权也可以重新抢占.
yield()方法是Thread类提供的一个静态方法,它会让当前执行的线程暂停,但又不会阻塞当前线程,让线程进入就绪状态.
public class YieldThreadDemo {
/**
* 执行次数.
*/
public static final int MAX_TURN = 100;
/**
* 执行编号.
*/
public static AtomicInteger index = new AtomicInteger(0);
/**
* 执行总次数.
*/
private static Map<String, AtomicInteger> metric = new HashMap<>();
/**
* 输出线程的总次数.
*/
private static void printMetric() {
System.out.println("metric=" + metric);
}
static class YieldThread extends Thread {
static int threadNum = 1;
public YieldThread() {
super("YieldThread--" + threadNum);
threadNum++;
metric.put(this.getName(), new AtomicInteger(0));
}
@Override
public void run() {
for (int i = 1; i < MAX_TURN && index.get() < MAX_TURN; i++) {
System.out.println(getName() + "线程优先级:" + getPriority());
index.getAndIncrement();
metric.get(this.getName()).getAndIncrement();
if (i % 2 == 0) {
Thread.yield();
}
printMetric();
System.out.println(getName() + "运行结束");
}
}
}
public static void main(String[] args) throws InterruptedException {
YieldThread yieldThread1 = new YieldThread();
yieldThread1.setPriority(Thread.MAX_PRIORITY);
YieldThread yieldThread2 = new YieldThread();
yieldThread2.setPriority(Thread.MIN_PRIORITY);
System.out.println("启动线程.");
yieldThread1.start();
yieldThread2.start();
Thread.sleep(1000);
}
}
线程的daemon操作
java里的线程主要分为守护线程和用户线程.守护线程可以理解为后台线程.主要在后台提供一下服务.比如垃圾回收线程.
1:守护线程的基本操作
实例属性daemon:保存一个线程实例的守护状态,默认为false.表示默认为用户线程.
实例方法setDaemon():获取线程的守护状态,判断这个线程是不是守护线程.
2:守护线程的操作演示
public class DaemonThread {
//守护线程类.
static class daemonThread extends Thread {
public daemonThread() {
super("daemonThread");
}
@Override
public void run() {
System.out.println("--daemon线程开始执行");
for (int i = 1; ; i++) {
System.out.println(">>轮次" + i);
System.out.println("--守护状态为" + Thread.currentThread().isDaemon());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
daemonThread daemonThread = new daemonThread();
daemonThread.setDaemon(true);
daemonThread.start();
Thread thread = new Thread(() -> {
System.out.println(">>用户线程开始执行");
for (int i = 0; i < 4; i++) {
System.out.println(">>轮次" + i);
System.out.println(">>线程状态为" + Thread.currentThread().isDaemon());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("用户线程结束.");
}, "userThread");
thread.start();
System.out.println("守护状态为"+Thread.currentThread().isDaemon());
System.out.println("线程结束");
}
}
3:守护线程与用户线程的关系
从是否守护线程来分,分为用户线程和守护线程.本质区别上,二者与jvm虚拟机进程终止方向不同.用户线程和jvm进程属于主动关系.如果用户线程全部终止,jvm进程也会终止.守护线程和jvm进程属于被动关系,jvm进程终止了,所有的守护线程也会终止.
4:守护线程的要点
用户线程必须在启动之前就设置为守护线程,启动之后不能再将用户线程设置为守护线程,否则jvm会抛出一个InterruptedException异常.
守护线程存在被jvm强行终止的风险.所以守护线程尽量不要去访问系统资源,如文件句柄数据库连接等.
守护线程创建的线程也是守护线程.
好多事情在没有做之前总觉着很简单,但是做起来了才发现.总结才是最难得.但是还是有很多收获吧.坚持下去,虽然不懂,但是在次回首,会发现收获到了意向不到的快乐.
如果喜欢的话,大家可以搜索一下微信公众号. 心有九月星辰
标签:Thread,核心,System,static,线程,println,原理,public From: https://blog.csdn.net/m0_68082638/article/details/140647033