长沙营销网站建设公司,北京网站设计公司sx成都柚米科技15,辽阳微网站建设,个人网站模板html免费Thread类
位于java.lang包下的Thread类是非常重要的线程类#xff0c;它实现了Runnable接口#xff0c;学习Thread类包括这些相关知识#xff1a;线程的几种状态、上下文切换#xff0c;Thread类中的方法的具体使用。 线程#xff1a;比进程更小的执行单元#xff0c;每…Thread类
位于java.lang包下的Thread类是非常重要的线程类它实现了Runnable接口学习Thread类包括这些相关知识线程的几种状态、上下文切换Thread类中的方法的具体使用。 线程比进程更小的执行单元每个进程可能有多条线程线程需要放在一个进程中才能执行线程由程序负责管理而进程则由系统进行调度 多线程的理解并行执行多个条指令将CPU时间片按照调度算法分配给各个线程实际上是分时执行的只是这个切换的时间很短用户感觉到同时而已
线程的状态
线程从创建到最终的消亡要经历若干个状态一般来说包括以下几个状态
创建(new)就绪(runnable)运行(running)阻塞(blocked)、主动睡眠(time waiting)、等待唤醒(waiting)消亡(dead) 当需要新起一个线程来执行某个子任务时就创建了一个线程。但是线程创建之后不会立即进入就绪状态因为线程的运行需要一些条件比如内存资源譬如程序计数器、Java栈、本地方法栈都是线程私有的所以需要为线程分配一定的内存空间只有线程运行需要的所有条件满足了才进入就绪状态。
当线程进入就绪状态后不代表立刻就能获取CPU执行时间也许此时CPU正在执行其他的事情因此它要等待。当得到CPU执行时间之后线程便真正进入运行状态。
线程在运行状态过程中可能有多个原因导致当前线程不继续运行下去比如用户主动让线程睡眠睡眠一定的时间之后再重新执行、用户主动让线程等待或者被同步块给阻塞此时就对应着多个状态time waiting睡眠或等待一定的事件、waiting等待被唤醒、blocked阻塞。
当由于突然中断或者子任务执行完毕线程就会被消亡。
原文链接
创建线程的三种方式
通过继承Thread类本身
class MyThread extends Thread {Overridepublic void run() {. . .}
}//启动线程
MyThread myThread new MyThread ();
new MyThread().start();
实现Runnalbe接口 实现Runnalbe接口重载Runnalbe接口中的run()方法。然后可以分配该类的实例在创建 Thread 时作为一个参数来传递并启动。
class runnable implements Runnable {Overridepublic void run() {. . .}
}//启动线程
MyRunnable runnable new MyRunnable();
new Thread(runnable).start();
使用匿名方法类
new Thread(new Runnable() {Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}tvThreadResult.setText(线程执行结果);}
}).start();
原文链接
上下文切换
对于单核CPU来说对于多核CPU此处就理解为一个核CUP在某一个时刻只能运行一个线程当在运行一个线程的过程中去运行另外一个线程这个就叫做线程上下文切换。
由于可能当前的线程并没有执行完所以在切换时需要保存线程的运行状态以便下次线程切换回来的时候能够以上次状态去继续运行举个简单的列子比如一个线程A正在读取某个文件的内容读取到一半的时候此时CPU需要切换线程去执行线程B当再次切换回来执行A的时候我们不希望线程A从头开始读取因此需要记录线程A的运行状态下次线程回复的时候我们需要知道线程执行到第几条指令了搜易需要记录程序计数器的值另外比如说线程正在进行某个计算的时候被挂起了那么下次继续执行的时候需要知道之前挂起时变量的值时多少因此需要记录CPU寄存器的状态。所以一般来说线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据。
对于线程的上下文切换其实就是存储和回复CPU状态的过程他使得线程能从断点处恢复执行。
虽然多线程可以使得任务执行的效率得到提升但是由于在线程切换时同样会带来一定的开销代价并且多个线程会导致系统资源占用的增加所以在进行多线程编程时要注意这些因素。
Thread类中的方法
Thread类实现了Runnable接口在Thread类中有一些比较关键的属性比如name是表示Thread的名字可以通过Thread类的构造器中的参数来指定线程名字priority表示线程的优先级最大值为10最小值为1默认值为5daemon表示线程是否是守护线程target表示要执行的任务。
下面是Thread类中常用的方法 start() start()用来启动一个线程当调用start()方法后系统才会开启一个新的线程来执行用户定义的子任务在这个过程中会为相应的线程分配需要的资源。 run() run()方法是不需要用户来调用的当通过start()方法启动一个线程之后当线程获得了CPU执行时间便进入run方法体去执行具体的任务。注意继承Thread类必须重写run()方法在run()方法中定义具体要执行的任务。 sleep() sleep方法有两个重载版本
sleep(long millis) //参数为毫秒sleep(long millis,int nanoseconds) //第一参数为毫秒第二个参数为纳秒
sleep方法相当于让当前线程睡眠交出CPU让CPU去执行其他的任务
当前线程调用sleep()方法进入阻塞状态后在其睡眠期间该线程不会获得执行机会即是系统中没有其他可执行线程因此sleep方法常用来暂停程序执行。
但是有一点需要注意sleep()方法不会释放锁也就是说如果当前线程持有某个对象的锁调用sleep()方法其他线程就无法访问这个对象。
interrupt()
interrupt()方法解释为中断线程实际是为了对线程做一个中断标记但是线程还是可能还是会执行不立即不强制默认不终止。
interrput()方法是替换stop()方法stop()方法已经弃用为什么弃用呢 是这样线程是一点一点执行任何时间都有可能发生线程切换任何时间都可以调用stop()方法这个线程就会立即停止可以产生非常随机的中间状态比如在某个时间切到别的线程再也切不回来了比如正在改某一个对象时线程停止了会造成不可预估的影响。
所以我们要使用interrupt()方法让程序去判断在什么时候中断当前线程这样就能保证代码的健壮性和程序的可控性。
既然interrupt()不能立即停止线程那么怎么才能让线程按照我们的要求停止呢 这里我要介绍俩个方法
isInterrupted()Thread.interrupted()
用法
//用于判断当前线程是否为中断状态不会重置状态
if(isInterrupted()){//做一些收尾工作return ;
}
//用于判断当前线程是否为中断状态,先调用isInterrupted(boolean ClearInterrupted)方法然后重置状态true变为falsefalse还是false
if(Thread.interrupted()){//做一些收尾工作return ;
}
而且interrupt()可以打断睡眠状态立即抛出异常。 //判断是否中断线程if(Thread.interrupted()){ //检查当前的线程//收尾工作}try {Thread.sleep(2000);} catch (InterruptedException e) {//收尾工作}
yield()
yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法它也可以让当前正在执行的线程暂停但它不会阻塞该线程它只是将该线程转入到就绪状态。即让当前线程暂停一下让系统的线程调度器重新调度一次完全可能的情况是当某个线程调用了yield()方法暂停之后线程调度器又将其调度出来重新执行。
调用yield方法会让当前线程交出CPU权限让CPU去执行其他的线程。它跟sleep方法类似同样不会释放锁。但是yield不能控制具体的交出CPU的时间另外当某个线程调用了yield()方法之后只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。
注意调用yield方法并不会让线程进入阻塞状态而是让线程重回就绪状态它只需要等待重新获取CPU执行时间这一点是和sleep方法不一样的。
join()
join方法有三个重载版本
join()join(long millis) //参数为毫秒join(long millis,int nanoseconds) //第一参数为毫秒第二参数为纳秒 假如在main线程中调用thread.join()方法则main()方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join()方法则等待thread执行完毕如果调用的是指定了时间参数的join方法则等待一定的事件。 代码示例
public class ThreadDemo {private int i 0 ;public static void main(String[] args) {ThreadDemo threadDemo new ThreadDemo() ;System.out.println(进入线程Thread.currentThread().getName());MyThread thread1 threadDemo.new MyThread() ;thread1.start();System.out.println(线程等待Thread.currentThread().getName());try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(线程继续执行Thread.currentThread().getName());}class MyThread extends Thread{Overridepublic void run() {synchronized (ThreadDemo.class){i ;System.out.println(线程 Thread.currentThread().getName() i i);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(线程 Thread.currentThread().getName() --睡醒 );}}}}
获取线程属性的几个方法
getId() 得到线程的IDgetName和setName 用来得到或者设置线程名称。getPriority和setPriority 用来获取和设置线程优先级。setDaemon和isDaemon 用来设置线程是否成为守护线程和判断线程是否是守护线程。守护线程和用户线程的区别在于守护线程依赖于创建它的线程而用户线程不依赖举个简单的例子如果在main()线程中创建一个守护线程当main()方法执行结束之后守护线程也会随之消亡。而用户线程不会用户线程会一直运行直到运行完毕在JVM中像垃圾收集器线程就是守护线程。currentThread() 用来获取当前的线程 8.wait()、notify()、notifyAll() wait()、notify()、notifyAll()这三个方法不是Thread类中的方法是Object本地的final方法但是多线程中也是不可或缺的。
wait()、notify()、notifyAll()和synchronized是配合使用的。
wait()在synchronized中在对应monitor维护等待队列会把当前的锁让开其他线程也可以访问同一个synchronized里面的代码。
notify()会唤醒同一个monitor的wait()让monitor去唤醒notify()唤醒wait()不确定是哪一个所以一般不适用notify()这个方法。
notifyAll() 是唤醒同一个moitor所有的wait()被唤醒后需要到monitor的执行队列中等待等待拿锁拿锁后从wait()位置继续执行。