)
多线程一、线程1.什么是线程2.为什么要有线程3.进程与线程的关系与区别4.Thread类及创建方法4.1创建线程4.2线程构造方法与属性4.3.启动一个线程start方法4.4.中断一个线程isInterrupted()4.5.等待一个线程join()4.6.休眠当前线程sleep()5.线程状态································································一、线程1.什么是线程一个线程是一个执行流每个线程都可以按顺序执行自己的代码多个线程“同时”执行自己的代码。2.为什么要有线程创建销毁进程的开销大尤其是频繁操作引入线程创建销毁开销更加小更加快3.进程与线程的关系与区别1.进程包含线程每个进程都有一个或者多个线程2.进程与进程之间不共享资源进程是操作系统资源分配的基本单位而线程与线程之间共享资源所以当一个进程崩溃不会影响其他进程但是一个线程挂了可能其他线程整个进程都崩溃这就是线程安全相关问题及时捕获可能不会崩溃3.线程是CPU上调度执行的最小单位一个进程可以包含多个线程同一个进程内的多个线程共享进程资源在CPU上调度执行。4.一个进程中的线程共用一个文件描述符表和内存指针但是每个线程都有自己的调度相关的状态、优先级、上下文、记账信息一个进程并不是越多越好过多线程产生的调度开销也很大会拖慢程序的性能4.Thread类及创建方法4.1创建线程对于线程操作系统提供一些api(应用程序编程接口给程序员使用操作系统提供的原生线程api是c语言的而且不同操作系统的线程api不同java对上述内容进行统一封装如Thread类标准库中提供的类。方法1继承Thread类创建一个线程类classMythreadextendsThread{Overridepublicvoidrun(){while(true){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}publicclassdemo1{publicstaticvoidmain(String[]args){MythreadmythreadnewMythread();//start方法创建一个线程多一个执行流像是多一个人干活mythread.start();while(true){try{//sleep静态方法让当前线程暂时放弃CPU过1000毫秒后继续执行Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}System.out.println(hello main);}}}运行结果显示有时候先输出main有时先输出thread,这是因为多个线程的调度顺序是随机的无法预测唯一可以做的就是设置优先级但是对于操作系统也不一定严格执行。方法2实现Runnable接口classMyRunnableimplementsRunnable{Overridepublicvoidrun(){while(true){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}publicclassdemo2{publicstaticvoidmain(String[]args){MyRunnablerunnablenewMyRunnable();ThreadthreadnewThread(runnable);thread.start();while(true){System.out.println(hello main);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}方法3使用内部类操作方法1publicclassdemo3{publicstaticvoidmain(String[]args){ThreadthreadnewThread(){Overridepublicvoidrun(){while(true){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}};thread.start();while(true){System.out.println(hello main);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}对于匿名内部类Thread threadnew Thread(){};1.创建一个Thread的子类子类匿名2.{}中写子类的属性方法3.创建匿名内部类的实例并且把实例赋值给thread方法4使用内部类操作方法2publicclassdemo4{publicstaticvoidmain(String[]args){RunnablerunnablenewRunnable(){Overridepublicvoidrun(){while(true){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}};ThreadtnewThread(runnable);t.start();while(true){System.out.println(hello main);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}方法5使用lambda表达式创建Runnable子类对象publicclassdemo5{publicstaticvoidmain(String[]args){ThreadthreadnewThread(()-{while(true){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}},t1);thread.start();while(true){System.out.println(hello main);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}lambda“匿名函数”主要需要方法但必须依托类存在使用“函数式接口”简化代码()- 创建一个匿名的函数式接口的子类并且创建出对应实例重写方法lambda访问外部变量的限制如果把在lambda中使用的变量定义成局部变量是否可以如果要把变量定义成局部变量则这个变量不能修改或者直接设置成final。这是由于变量捕获因为lambda是回调函数系统创建线程后才会执行可能当main主线程执行完局部变量都销毁了而lambda还需要使用所以java会把变量拷贝一份到lambda里面这样即使外面变量销毁也不影响lambda里面使用而拷贝也就意味着变量不能进行修改。如果这个局部变量是个引用类型那么这个引用本身不可以修改不能指向其它对象而对象本体是可以修改的因为对象本体是JVM垃圾回收机制管理不会随着main函数结束而销毁。如果是成员变量呢将变量改为成员变量就不会报错因为此时不再是“变量捕获”语法而是“内部类访问外部类的成员”语法。lambda本质是一个函数式接口相当于一个内部类isFinished就是外部类的成员内部类本身就可以访问外部类的成员成员变量生命周期是由垃圾回收GC管理的所以lambda不用担心销毁就不必拷贝就可以修改了。4.2线程构造方法与属性Java Thread 常见构造方法构造方法签名方法说明核心参数解释Thread()方法1创建一个新的线程对象线程名称为默认格式如Thread-0无目标任务需重写run()方法无参数Thread(Runnable target)方法2创建一个新线程将Runnable接口的实现类作为线程执行的目标任务target实现了Runnable接口的对象线程启动后会执行该对象的run()方法Thread(String name)创建指定名称的线程对象便于调试和日志排查无目标任务name自定义的线程名称如业务处理线程-1Thread(Runnable target, String name)方法5创建指定目标任务且指定名称的线程最常用的构造方法之一target线程执行的任务name线程自定义名称Thread(ThreadGroup group, Runnable target)了解将线程归属到指定的线程组并指定执行任务group线程所属的ThreadGroup用于批量管理线程target执行任务常见属性属性获取方法数据类型属性说明getId()long获取线程的唯一IDgetName()/setName(String name)String获取/设置线程名称getPriority()/setPriority(int newPriority)int获取/设置线程优先级getState()Thread.State获取线程状态isDaemon()/setDaemon(boolean on)boolean判断/设置是否为守护线程isAlive()boolean判断线程是否存活isInterrupted()boolean判断线程是否被中断interrupted()静态方法boolean判断当前线程是否被中断重点属性1)后台线程|isDaemon()/setDaemon(boolean on)|boolean|判断/设置是否为守护线程后台进程前台进程例如我们自己创建的线程包括main主线程但我们可以通过setDaemon方法将它改为后台线程后台进程例如JVM自带的具有特殊功能的线程跟随整个进程持续执行。JVM会在一个进程的所有前台线程结束后才结束运行而后台线程对结束无影响2)是否存活简单理解为run方法运行结束|isAlive()|boolean| 判断线程是否存活 |java中创建的Thread对象与系统的线程一一对应但是两者的生命周期并不相同可能Thread对象依然存在但是对应的系统的线程已经销毁上面for循环执行完对象thread可能依然存在4.3.启动一个线程start方法调用start方法才真正创建一个线程。每个Thread对象start是一次性的进行调用系统的api每次创建新线程都得创建一个新的Thread对象。run与start方法的区别作用功能不同run方法的作用是描述线程具体要执行的任务start方法的作用是真正的去申请系统线程运行结果不同run方法是一个类中的普通方法主动调用和调用普通方法一样会顺序执行一次start调用方法后, start方法内部会调用Java 本地方法封装了对系统底层的调用真正的启动线程并执行run方法中的代码run 方法执行完成后线程进入销毁阶段。4.4.中断一个线程isInterrupted()中断即线程停止不会再恢复lambda的定义在new Tread之前所以不能直接在里面使用但是java的Thread对象中提供了现成的变量我们可以使用Thread.currentThread()静态方法哪个线程中调用获取的就是哪个线程的Thread引用java的线程终止不是一个”强制性“措施运行下面的程序结果发现虽然写了终止操作但是仍然循环输出是因为正常情况下interrupet方法把isinterrupted方法内部的标志位设置为true由于上述代码把sleep唤醒了把isinterrupted方法内部的标志位设置回false因此循环继续执行。当在catch中写throw new RuntimeException(e)会抛出异常不会继续执行写break会直接终止。所以对于线程立即结束等会结束还是不结束直接忽视终止信号程序员可以自行选择。publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadthreadnewThread(()-{while(!Thread.currentThread().isInterrupted()){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){// throw new RuntimeException(e);//break;}}});thread.start();Thread.sleep(1000);thread.interrupt();//将sleep提前唤醒把isinterruptted标志位设置为false,所以catch中不写就会继续循环执行}4.5.等待一个线程join()在主线程中调用t.jion就是让主线程等待t线程先结束在join中提供参数可以设置一个时间上限当超过这个参数t还没结束主线程就不会继续等待。4.6.休眠当前线程sleep()代码执行到sleep()相当于让当前线程让出cpu的资源后续时间到了的时候需要操作系统内核把这个线程重新调到cpu上才能继续执行5.线程状态NEW:安排工作还未开始行动new了Thread对象还未start()TERMINATED工作完成了 线程的run方法执行完毕或抛出未捕获的异常内核中线程结束但Thread对象还在RUNNABLE:工作中或者即将开始工作线程在cpu上运行或者随时可以去cpu上运行TIMED_WAITING有时间上限的阻塞不参与cpu调度thread.join(1000),Thread.sleep(1000)WAITING无时间上限阻塞不参与cpu调度,thread.join()publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadthreadnewThread(()-{for(inti0;i4;i){System.out.println(hello thread);try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}});Threadthread2newThread(()-{try{Thread.sleep(500);thread.join();}catch(InterruptedExceptione){thrownewRuntimeException(e);}System.out.println(线程thread2开始执行);});System.out.println(thread2初始状态thread2.getState());//NEW安排了工作还未行动thread.start();thread2.start();//设置一个专门监控thread2状态的线程newThread(()-{while(thread2.isAlive()){System.out.println(thread2状态thread2.getState());try{Thread.sleep(100);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}System.out.println(thread2最终状态thread2.getState());}).start();}}