当前位置: 首页 > news >正文

.net网站 还原数据库备份58同城本地网页版

.net网站 还原数据库备份,58同城本地网页版,甘肃省住房和建设厅网站,淮安 网站建设#x1f490;个人主页#xff1a;初晴~ #x1f4da;相关专栏#xff1a;多线程 / javaEE初阶 上篇文章我们简单介绍了什么是进程与线程#xff0c;以及他们之间的区别与联系#xff0c;实际应用中还是以多线程编程为主的#xff0c;所以这篇文章就让我们更加深入地去剖… 个人主页初晴~ 相关专栏多线程 / javaEE初阶 上篇文章我们简单介绍了什么是进程与线程以及他们之间的区别与联系实际应用中还是以多线程编程为主的所以这篇文章就让我们更加深入地去剖析多线程编程的具体应用吧 目录 一、初识Thread类 1、创建线程 1继承Thread类 2实现Runnable接口 3匿名内部类 2、多线程的优势-增加运⾏速度 二、 Thread 类及常⻅⽅法 1、 Thread 的常⻅构造⽅法 2、 Thread 的⼏个常⻅属性 三、线程的状态 四、线程的核心操作 1、启动一个线程-start 2、获取当前线程引用 3、休眠当前线程 4、线程的中断终止 5、线程等待-join 总结 一、初识Thread类 ⼀个线程就是⼀个 执⾏流. 每个线程之间都可以按照顺序执⾏⾃⼰的代码. 多个线程之间 同时 执⾏着多份代码. Java 的线程 和 操作系统线程 的关系 线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对⽤⼾层提供了⼀些 API 供⽤⼾使⽤(例如 Linux 的 pthread 库). 1、创建线程 Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进⾏了进⼀步的抽象和封装 接下来我们就来看一下创建线程的几种写法吧 1继承Thread类 编写的MyThread需要继承Thread类不需要导包因为Thread类是java.lang中内置的类。 继承不是主要目的主要是为了重写Thread类中的run方法在其中写入所创建线程需要执行的逻辑语句。 若要让线程运行需先实例化编写的MyThread类接着调用start方法就会在进程内部创建一个新的线程新的线程就会执行刚才run里的代码。 具体代码如下 class MyThread extends Thread{//重写run方法Overridepublic void run(){//线程执行的逻辑System.out.println(Hello World!);} } public class Main {public static void main(String[] args) {MyThread myThreadnew MyThread();//创建线程myThread.start();} } 这个代码运行起来是一个进程但这个进程包含了两个线程。 1、调用main方法的线程被称为“主线程”之前提过一个进程中至少有一个线程这个线程就是主线程 2、调用myThread.start()方法时会手动创建一个新的线程 主线程和新线程会并发/并行地在CPU上运行 不过上述代码还不能很好地体现多线程编程的并发性与随机性接下来用一个更加形象的代码表示一下 class MyThread extends Thread{//重写run方法Overridepublic void run(){while (true){System.out.println(Hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}} } public class Main {public static void main(String[] args) {MyThread tnew MyThread();//创建线程t.start();while (true){System.out.println(Hello main);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}} }可以发现多个线程之间谁先去CPU上调度执行这个过程是 “不确定的”这个调度顺序取决于操作系统内核里的 “调度器”调度器里有一套规则但是对于应用程序开发无法进行干预也无法察觉因此把这个过程近似于 “随机”多线程的运行调度也被称之为 “抢占式执行” 注意 上述代码中并没有直接手动调用run方法但是也被执行了。像run这种用户手动定义了但是没有手动调用最终被系统/库/框架调用执行了的方法被称为“回调函数call back”。 2实现Runnable接口 Runnable就是用来描述“要执行的任务”是什么 class MyRunnable implements Runnable{Overridepublic void run() {System.out.println(Hello Runnable!);} } public class Main {public static void main(String[] args) {MyRunnable runnablenew MyRunnable();Thread myThreadnew Thread(runnable);//创建线程myThread.start();} } 通过Thread创建线程而线程要执行的任务是通过Runnable来描述的而不是通过Tread自己来描述这样能起到一定的“解耦合”的作用便于代码后期维护。 Runnable只是描述了一个任务并不与“线程”强相关后续执行这个任务的载体可以是线程也可以是其他东西比如线程池、虚拟线程协程等一定程度上提高了代码的复用率。 对⽐上⾯两种⽅法: • 继承 Thread 类, 直接使⽤ this 就表⽰当前线程对象的引⽤. • 实现 Runnable 接⼝, this 表⽰的是 MyRunnable 的引⽤. 需要使⽤Thread.currentThread() 3匿名内部类 匿名指没有类名内部类指定义在其它类内部的类匿名内部类一般就是“一次性”使用的类用完就丢掉相对来说内聚性会更好一些 匿名内部类创建 Thread ⼦类对象 // 使⽤匿名类创建 Thread ⼦类对象 Thread tnew Thread(){Overridepublic void run() {System.out.println(使⽤匿名类创建 Thread ⼦类对象);} }; 这个方法本质上和1是一致的具体原理如下 1、定义匿名内部类这个类是Thread的子类 2、类的内部重写了父类的run方法 3、创建了一个子类的实例并把实例的引用赋值给了t 匿名内部类创建 Runnable ⼦类对象 //使⽤匿名类创建 Runnable ⼦类对象 Thread t2new Thread(new Runnable() {Overridepublic void run() {System.out.println(使⽤匿名类创建 Runnable ⼦类对象);} }); lambda表达式创建 Runnable 子类对象 Thread t3new Thread(()- System.out.println(使⽤lambda表达式创建 Runnable ⼦类对象)); Thread t4new Thread(()-{System.out.println(使用lambda表达式创建 Runnable 子类对象); }); lambda表达式本质上就是匿名内部类的更简化的写法很多时候写匿名内部类都不是为了写“类”而是写类中的“方法”而lambda就可以避开类而直接描述其中的run方法 2、多线程的优势 可以观察多线程在⼀些场合下是可以提⾼程序的整体运⾏效率的。 • 使⽤ System.nanoTime() 可以记录当前系统的 纳秒 级时间戳. • serial 串⾏的完成⼀系列运算. concurrency 使⽤两个线程并⾏的完成同样的运算 public class ThreadAdvantage {// 多线程并不⼀定就能提⾼速度可以观察count 不同实际的运⾏效果也是不同的private static final long count 10_0000_0000;public static void main(String[] args) throws InterruptedException {// 使⽤并发⽅式concurrency();// 使⽤串⾏⽅式serial();}private static void concurrency() throws InterruptedException {long begin System.nanoTime();// 利⽤⼀个线程计算 a 的值Thread thread new Thread(new Runnable() {Overridepublic void run() {int a 0;for (long i 0; i count; i) {a--;}}});thread.start();// 主线程内计算 b 的值int b 0;for (long i 0; i count; i) {b--;}// 等待 thread 线程运⾏结束thread.join();// 统计耗时long end System.nanoTime();double ms (end - begin) * 1.0 / 1000 / 1000;System.out.printf(并发: %f 毫秒%n, ms);}private static void serial() {// 全部在主线程内计算 a、b 的值long begin System.nanoTime();int a 0;for (long i 0; i count; i) {a--;}int b 0;for (long i 0; i count; i) {b--;}long end System.nanoTime();double ms (end - begin) * 1.0 / 1000 / 1000;System.out.printf(串⾏: %f 毫秒%n, ms);} } 二、 Thread 类及常⻅⽅法 Thread 类是 JVM ⽤来管理线程的⼀个类换句话说每个线程都有⼀个唯⼀的 Thread 对象与之关联。 ⽤我们上⾯的例⼦来看每个执⾏流也需要有⼀个对象来描述类似下图所⽰⽽ Thread 类的对象就是⽤来描述⼀个线程执⾏流的JVM 会将这些 Thread 对象组织起来⽤于线程调度线程管理。 1、 Thread 的常⻅构造⽅法 代码事例 Thread t1 new Thread(); Thread t2 new Thread(new MyRunnable()); Thread t3 new Thread(这是我的名字); Thread t4 new Thread(new MyRunnable(), 这是我的名字); 如果不给线程起名字那么默认就会是叫做Thread-0Thread-1…… 给线程起名字并不会影响线程的执行效果不过起一个合适的名字更有利于调试程序 ThreadGroup线程组是把多个线程放在一组里方便统一地设置线程的一些属性。不过现在很少会使用线程组了线程相关属性用到也不多更多的是会使用“线程池” 2、 Thread 的⼏个常⻅属性 • ID 是JVM自动分配的是线程的唯⼀标识不同线程不会重复 • 名称是各种调试⼯具⽤的Thread对象的身份标识 • 状态表⽰线程当前所处的⼀个情况下⾯我们会进⼀步说明 • 优先级⾼的线程理论上来说更容易被调度到 • 关于后台线程需要记住⼀点JVM会在⼀个进程的所有⾮后台线程结束后才会结束运⾏。 • 是否存活即简单的理解为 run ⽅法是否运⾏结束了 • 线程的中断问题下⾯我们进⼀步说明 关于前台线程与后台线程 后台线程如果这个线程执行过程中不能阻止进程的结束虽然线程在执行着但是进程要结束了此时这个线程也会随之被带走这样的线程就被称为“后台线程”也被称为“守护线程”。 前台线程如果某个线程在执行过程中能够阻止进程结束就被称为“前台线程” 一个线程中可以有多个前台线程创建的线程默认是前台线程必须所有的前台线程都结束进程才能结束 关于线程是否存活 isAlive返回值为true表示还存活false表示线程没了 代码中new Thread对象的生命周期与内核中实际线程的生命周期不一定是一致的可能会出现Thread对象存在但是内核中线程不存在的情况如 1调用start前此时内核中还未创建线程 2线程中的run执行完毕内核的线程销毁但此时Thread对象任然存在 注意不存在Thread对象不存在而线程还存在的情况 三、线程的状态 线程的状态是⼀个枚举类型 Thread.State反映了线程生命周期的不同阶段了解这些更有利于对线程的调试与优化以下是线程的六种主要状态 • NEW初始: Thread对象已创建但是内核的线程还没有还未调用start方法安排了⼯作, 还未开始⾏动 • RUNNABLE可运行: 线程的start方法一旦被调用就会进入RUNNABLE状态就绪状态可能正在CPU上运行也可能在等待CPU分配资源以便运行 • BLOCKED阻塞: 因为锁竞争而引起的阻塞等待的状态当线程试图获取某一对象的锁而该对象的锁正被其它线程占有时就会进入BLOCKED状态一般指线程因同步操作synchronized而被阻塞的状态 • WAITING等待: 当线程调用了Object.wait(),Thread.join(),LockSupport.park()等方法时就会进入WAITING状态此时线程不会争夺CPU资源一直等待直到其它线程发出对应的通知信息如notify或notifyAll时重新恢复RUNNABLE状态没有超时时间的阻塞等待如果没有收到通知将会一直等待下去 • TIMED_WAITING超时等待: 与WAITING状态类似但是是有超时时间的等待当调用Object.wait(long timeout),Thread.join(long),Thread.sleep(long millis),LockSupport.parkNanos(),LockSupport.parkUntil()等方法时线程会进入TIMED_WAITING状态线程将会等待直到被唤醒或超时 • TERMINATED终止: 当线程的run方法执行完毕或者由于某些原因如抛出为捕获的异常而提前结束时线程进入TERMINATED状态当前Thread对象虽然还在但是内核的线程已经被销毁了线程已经结束了终止的线程无法被重启 可以形象的类比于以下状态 上述线程状态可以通过jdk自带的jconsole来观察 学习线程的状态主要就是为了调试与优化比如遇到某个线程没有正常运行时就可以观察对应线程的状态来确定是否是由于一些原因导致线程进入了阻塞状态 四、线程的核心操作 1、启动一个线程-start 之前我们已经看到了如何通过覆写 run ⽅法创建⼀个线程对象但线程对象被创建出来并不意味着线程就开始运⾏了。 • 覆写 run ⽅法是提供给线程要做的事情的指令清单 • 线程对象可以认为是把 李四、王五叫过来了 • ⽽调⽤ start() ⽅法就是喊⼀声”⾏动起来“线程才真正独⽴去执⾏了。 调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程 注意要区分好 run与 start的关系 run线程的入口描述了线程 要执行的任务 start调用了 系统api在系统内核中 真正创建了线程创建PCB加入到链表中 也就是说如果直接调用run方法虽然也能执行run方法内的程序但也只会在调用它的线程中执行是不会创建新的线程的这样就背离多线程编程的初心了 注意 start对于一个Thread对象只能调用一次java中的Thread对象与内核中的线程是“一对一”的关系因此不存在一个线程终止后再通过调用start重新执行的情况 这是由于一个Thread对象能够对应到多个线程的话管理起来就会非常麻烦JVM的设计实现就会非常复杂了。因此java中希望一个Thread对象只能对应到系统中的一个线程这样在调用start后就可以通过线程的状态来判断是否能成功创建。 如果一个Thread对象是没有调用过start的此时就会处于NEW状态此时调用start就可以顺利地执行创建出线程了。而如果已经调用过start线程就会进入其它状态只要不是处于NEW状态接下来执行start都会抛出异常了 2、获取当前线程引用 想在某个线程中获取到自身的Thread对象的引用就可以通过currentThread()来获取 任何线程都可以通过这样的操作获取线程的引用 3、休眠当前线程 也是我们⽐较熟悉⼀组⽅法有⼀点要记得因为线程的调度是不可控的所以这个⽅法只能保证实际休眠时间是⼤于等于参数设置的休眠时间的 public class ThreadDemo {public static void main(String[] args) throws InterruptedException {System.out.println(System.currentTimeMillis());Thread.sleep(3 * 1000);System.out.println(System.currentTimeMillis());} } 线程执行sleep就会使这个线程不参与CPU调度从而把CPU资源让出来给别人使用。像sleep这样的操作也被称之为“放权”放弃使用CPU的权利 在有些开发场景中如果某个线程CPU占用率过高就可以通过sleep来改善。虽然线程的优先级就可以对其产生影响但影响是比较有限的通过sleep可以更加明显地影响到CPU占用 4、线程的中断终止 在工作中我们可能会因为领导的一通电话而不得不停下手头的工作去做别的事。线程运行时可能也会遇到类似问题有时我们可能会因为某些原因而需要提前结束线程的运行该如何停止呢这就涉及到停止线程的方式了。 如果有两个线程a和bb正在运行a想要b结束运行其实核心就是a要想办法让b的run方法更快地执行完毕此时b自然就结束了。而不是说b的run执行一半a直接强行把b结束了。java中结束线程的方法是比较“温柔”的并不是直接粗暴的。因为如果强制结束某个线程的话可能导致其逻辑未完全执行对应的结果数据是个“半成品”从而影响程序最终的结果这样肯定是不合理的。 1、一个简单的做法是使用自定义的标志位 public class Demo {//设置全局变量isQuit作为标志位public static boolean isQuitfalse;public static void main(String[] args) throws InterruptedException {Thread tnew Thread(()-{while (!isQuit){System.out.println(hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(4000);System.out.println(main线程尝试终止 t 线程);//通过修改isQuit变量的值就能终止 t 线程的执行了isQuittrue;}}注意如果把isQuit变成main方法的局部变量就会出现以下情况 我们可以看到lambda语句中的isQuit标红报错了。这与lambda表达式/匿名内部类的变量捕获操作有关。 isQuit与lambda表达式定义在一个作用域内lambda的内部是可以访问到lambda外部与lambda同一作用域的变量的。但是lambda的变量是有要求的能够捕获的变量得是final或者事实final即虽没有final修饰但并没有人修改而上述代码中的isQuit变量被修改过不是final/事实final导致lambda表达式无法通过变量捕获操作获取到它从而导致程序出现了错误 而当把isQuit写成成员变量后就成了内部类访问外部类的成员变量这本就是合法的因此就不会出现问题了 2、使用Thread自带的interrupted作为标志位   Thread中有一个boolean类型的成员变量interrupted它的初始值为false表示未被终止一旦其它线程调用了interrupt方法就会设置上述标志位值为true表示已被终止。 public class Main {public static void main(String[] args) throws InterruptedException {//下列的 lambda 的代码在编译器眼里出现在 Thread t 的上方//此时 t 还没有被定义Thread tnew Thread(()-{//先获取到线程的引用Thread currentThread Thread.currentThread();//两种判断方式都行while (!currentThread.isInterrupted()){//while(!Thread.interrupted())System.out.println(hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(3000);//在主线程中控制 t 线程被终止设置上述标志位t.interrupt();} }但是执行上述代码我们会发现程序运行是有问题的 可以看出程序运行抛出了一个RuntimeException异常。主要是由于判断isInterrupted()和执行打印操作的速度过快因此在整个循环中主要时间都是处于sleep状态中main调用interrupt()时不仅仅会设置标志位还会把sleep给唤醒假如sleep刚执行了100ms还剩900ms此时interrupt被调用sleep就会被强制唤醒并且抛出interruptedException异常。 又由于catch中的代码默认再次抛出了一个RuntimeException异常而这个异常没有被处理就会导致直接抛到JVM层面使得进程直接异常终止了 这时我们尝试将代码改为 这样不抛出新的异常而是输出一段语句是否就能让程序正常运行呢 显然是不能的我们可以看到在执行catch操作后线程并没有被终止仍然在不断地运行输出。 这是由于当sleep等阻塞函数被唤醒后会清空刚刚设置的interruptded标志位这样在线程的下次循环判断时程序就会认为标志位任未被设置从而继续执行下去了。 此时如果想要结束循环结束线程就需要在catch中加上return/break语句 这样线程就能正常被终止了 出现这样的现象主要是由于java中线程终止是一个相对“温柔”的过程并不是强行就终止。当a线程想让b线程终止时b可以自行决定是否要终止/是立即还是稍后这些都由b线程内部的代码来决定其他线程无权干涉。 比如 1如果b线程想无视a线程的终止请求就直接在catch中啥也不做b仍然会继续执行 2如果b线程相要立即结束就在catch中写入return或break此时b线程就会立刻结束 3如果b线程想要稍后结束就可以在catch上写入一些其他逻辑如释放资源清理一些数据提交一些结果……等收尾工作这些逻辑完成后再进行return/break操作 这些全都得益于sleep这类阻塞方法被强制唤醒时会清除标志位才能让b做出各种选择否则b将被强制结束无法写出让程序继续执行的代码了。这样可以给程序员更多的操作空间 5、线程等待-join 操作系统针对多个线程的执行是一个“随机调度”抢占式执行的过程。线程的调度执行是随机的我们无法确定两个线程的调度顺序但是可以控制谁先结束谁后结束。 线程等待就是确定两个线程的结束顺序通过让后结束的线程等待先结束的线程执行进入阻塞状态直到先结束的线程执行完毕此时阻塞解除后结束的线程开始执行。这样就能使两个线程的结束顺序得以确定 这时就可以使用join关键字实现线程等待 比如有两个线程ab此时在a线程中调用b.join意思就是让a线程等待b线程先结束a再继续执行。通俗的来讲就相当于是让b插入到a线程的执行过程中 不过也要注意a和b本质上还是两个线程依旧是并发执行只是确定了结束顺序 代码示例 public class Demo {public static void main(String[] args) throws InterruptedException {Thread tnew Thread(()-{System.out.println(t 线程开始执行);for (int i 0; i 3; i) {System.out.println(这是线程 t);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(t 线程执行结束);});t.start();//让主线程等待 t 线程System.out.println(main 线程开始等待);t.join();System.out.println(main 线程等待结束);} }可以很明显地看到主线程是先开始执行的但当执行到t.join()语句时主线程开始阻塞等待 t 线程的执行当 t 线程执行结束后join才会返回主线程才会继续执行后续代码。 不过如果 t 线程先执行完毕了然后主线程才开始join此时主线程不会出现阻塞等待而是会正常执行 注意         上述操作都是无参数的join方法就是“死等”只要被等待的线程没有执行完毕就会一直阻塞等待。这并不是一个好的选择因为一旦被等待的线程代码出现bug可能导致该线程迟迟无法结束从而使等待线程一直阻塞而无法继续执行其它操作了 方法如下 可以在join方法中加入参数来确定等待时间如果等待时间超过设定时间就会停止等待退出阻塞状态继续执行后续代码了。 方法汇总   那么本篇文章就到此为止了如果觉得这篇文章对你有帮助的话可以点一下关注和点赞来支持作者哦。作者还是一个萌新如果有什么讲的不对的地方欢迎在评论区指出希望能够和你们一起进步✊
http://www.eeditor.cn/news/125854/

相关文章:

  • 建网站 开发app乐趣浏览器app下载
  • 如皋建设工程局网站wordpress 图片下一页
  • 在线旅游网站昆山规划建设局网站
  • 建设银行贵金属网站常用的设计师网站
  • 电影网站制作教程及步骤聚财洋气三个字公司名字
  • 毕设做网站需要什么技术准备网络营销策划的内容
  • 注册公司域名后如何做网站wordpress盲注
  • 快速网站模板公司网页传奇游戏排行榜九点开服
  • 视频上传下载网站建设网站管理公司
  • 南屏网站建设桥梁建设工程网站
  • 普洱市交通建设集团官方网站怎样推广小程序
  • 成都 企业 网站制作新乡商城网站建设哪家好
  • 网站建站免费visual studio怎么做网页
  • 免费ppt模板网站哪个好用网站代理违法吗
  • 网站服务器的选择网站开发公司需要什么资质
  • 门户网站开发视频教学cdn wordpress
  • 织梦网站栏目调用网站建设一般报价多少
  • 达州网站开发qinsanw哪个旅游网站做的最好
  • 我要建立网站wordpress全站开启ssl
  • 个人网站模板 php中国建设监理网站
  • 团员建设网站安陆网站制作公司
  • 个人网站备案需要几天注册网站要语音验证码的有哪些
  • 怎样看出一个网站是那个公司做的中国排名前十的建筑公司
  • 网站建设石家庄快优wordpress查询码
  • 建站哪家好 discuzui设计网站建设是什么
  • 做网站找个人还是找公司江苏大汉建设实业集团网站
  • 怎么成立网站网站关键词优化排名公司
  • 沙井网站优化如何看网站的浏览量
  • 福田公司董事长seo是指什么职位
  • 公共法律服务网站建设总结棋牌app开发公司