网站类型定位重庆手机网站制作价格

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

网站类型定位,重庆手机网站制作价格,wordpress迁移数据,互联网公司排名榜文章目录 前言1. API2. 创建线程2.1. 继承 Thread类2.2. 实现 Runnable 接口2.3. 匿名内部类2.4. lambda2.5.其他方法 3. Thread类及其常见的方法和属性3.1. Thread 的常见构造方法3.2. Thread 的常见属性3.3. start() — 启动一个线程3.4. 中断一个线程3.5. 等待线程3.6. 休眠… 文章目录 前言1. API2. 创建线程2.1. 继承 Thread类2.2. 实现 Runnable 接口2.3. 匿名内部类2.4. lambda2.5.其他方法 3. Thread类及其常见的方法和属性3.1. Thread 的常见构造方法3.2. Thread 的常见属性3.3. start() — 启动一个线程3.4. 中断一个线程3.5. 等待线程3.6. 休眠当前线程 前言 上一节课我们主要引出了线程以及线程和进程的联系与区别从这篇博客开始我们开始编写代码了Let’s go 1. API 线程是操作系统提供的概念操作系统系统提供一些API供程序员使用。什么是 API 呢 API (Aoolication Programming Interface) 应用程序编程接口简单来说就是别人写了一些类 / 函数可以直接拿过来使用。 来源api 的来源很广泛例如 标准库第三方库其他的各种开源项目甚至是在工作中隔壁项目给你提供的代码。 并且呀操作系统提供的原生线程 api 是 C语言的并且不同的操作系统的线程 api 是不一样的。 为此 Java 对上述内容统一封装提供Thread (标准库中的类) 2. 创建线程 2.1. 继承 Thread类 class MyThread extends Thread{Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Hello thread!);}} } public class Demo1 {public static void main(String[] args) throws InterruptedException {// 方法一创建一个 MyThread 类继承 Thread重写 run 方法Thread thread new MyThread();thread.start();while (true){System.out.println(Hello main);Thread.sleep(1000);}} }上面便是创建一个 MyThread 类 继承 Thread重写run方法的形式下面我们来分别说一下。 class MyThread extends Thread{Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Hello thread!);}} }这段代码中是我们自己利用JVM自带的Thread类实现的一个MyThread并且重写了run方法当我们调用线程的时候就直接运行run方法的内容了无需调用。 为什么不需要调用 run()方法呢 因为 run() 相当于 回调函数。 例如在Java的数据结构中优先级队列执行比较规则如果使用 Comparator 接口方式就是 compare如果使用 Comparable接口方法就是 compareTo我们没有调用这个函数他就自己就调用了这就是回调函数 Thread.sleep(1000);这个方法是Thread 的静态方法参数是毫秒效果就是当前的线程放弃 CPU 资源休息一会时间过了再执行。为什么要进行这个操作呢因为如果不休眠的话那么 这两个线程会一直吃 CPU 的资源可能会造成 电脑的卡顿并且让 线程休息一下也便于我们观察现象。 这个方法会抛出 InterruptedException 异常需要我们 使用 try catch 进行捕获。因为 run()方法没有 throws 所以就不能向上抛出异常只能使用 try catch这个跟下面的main方法中的不太一样。 Thread thread new MyThread();这个就创建了一个线程 thread和main 线程一起执行。 thread.start();thread 线程 开始执行此处为什么不调用 thread.run()而是 thread.start() while (true){System.out.println(Hello main);Thread.sleep(1000);}这段代码是main线程中执行的逻辑跟上面 thread 几乎一模一样就是为了观察效果的。 效果显而易见我们发现 Hello main 和 Hello thread!运行的过程中是轮番打印的并且没有规律。 在这说一下如果调用的是 thread.run()而不是 thread.start()会出现什么现象。 public class Demo01 {public static void main(String[] args) throws InterruptedException {// 方法一创建一个 MyThread 类继承 Thread重写 run 方法Thread thread new MyThread();//thread.start();thread.run();while (true){System.out.println(Hello main);Thread.sleep(1000);}} }运行情况 相当于thread线程就没有运行因此不要这样写代码。 2.2. 实现 Runnable 接口 class MyRunnable implements Runnable{Overridepublic void run() {while (true){try {System.out.println(Hello thread!);Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}} } public class Demo02 {public static void main(String[] args) throws InterruptedException {Runnable runnable new MyRunnable();Thread thread new Thread(runnable);thread.start();while (true){System.out.println(Hello main!);Thread.sleep(1000);}} }Runnable runnable new MyRunnable();这个就是一个任务一段要执行的逻辑最终还是要通过 Thread 才能创建真正的线程那为什么这样子写呢或者就是这样子写有什么好处 可以做到解耦合 意思就是让要执行任务本身这个事情和 线程这个事可以 关联没有那么大从而后续如果要变更代码 (比如不通过线程执行这个任务而是通过其他方法…)那么采用 Runnable 这样的方案代码的修改就会更简单了。 与此相关的还有一个名词高内聚。 例如写了一个项目有很多代码有很多文件也有很多类以及很多的逻辑其中把有关联的各种代码放到一起只要把某个功能逻辑相关的东西都放到一起那么就称之为高内聚 反之如果是某个功能的代码东一块西一块的那么就是低内聚。 因此么我们在写代码的时候通常要做到高内聚低耦合。 现象跟上面的情况差不多。
2.3. 匿名内部类 利用 Thread 来使用 匿名内部类 public class Demo03 {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(){Overridepublic void run() {while (true){System.out.println(Hello thread!);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};thread.start();while (true){System.out.println(Hello main!);Thread.sleep(1000);}} }Thread thread new Thread(){};这个代码做了三个事情 创建了一个 Thread 的子类子类叫什么名字不知道他是个匿名{ } 里面就可以编写子类的定义代码子类里有哪些属性要有哪些方法重写父类哪些方法…都可以往里面写。创建了这个匿名内部类的实例并且把实例的引用赋值给了 t。 同理下面 Runnable 也是这个意思。 现象如下图所示 利用 Runnable 来实现 匿名内部类 public class Demo04 {public static void main(String[] args) throws InterruptedException {Runnable runnable new Runnable() {Overridepublic void run() {while (true){System.out.println(Hello Thread!);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};Thread thread new Thread(runnable);thread.start();while (true){System.out.println(Hello main!);Thread.sleep(1000);}} 使用匿名内部类的好处就是 少定义一些类。 一般如果某个代码是 “一次性的”就可以使用匿名内部类的写法。 2.4. lambda 其实 lambda 表达式的本质就是回调函数使用 () - {} public class Demo05 {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(()-{while (true){System.out.println(Hello Thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();while (true){System.out.println(Hello main);Thread.sleep(1000);}} }注意。没有 Runnable 的形式。 咱们上课主要就是使用 lambda 表达式的形式来创建线程。 2.5.其他方法 不止上面的四种方法还有两种一个是线程池另外一种是实现Callable接口等到后面我们再说。 3. Thread类及其常见的方法和属性 3.1. Thread 的常见构造方法 方法说明Thread()创建线程对象Thread(Runnable target)使用 Runnable 对象创建线程对象Thread(String name)创建线程对象并命名Thread(Runnable target,String name)使用 Runnable 对象创建线程对象并命名【了解】Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理分好的组即为线程组这个目前了解即可 其中第一个和第二种方法都已经演示过了直接演示第四个吧。 public class Demo06 {public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()-{while (true){System.out.println(Hello t1);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},t1);Thread t2 new Thread(()-{while (true){System.out.println(Hello t2);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},t2);t1.start();t2.start();while (true){System.out.println(Hello main);Thread.sleep(1000);}} }此时我们打开JDK自带的软件 jconsole (在JDK的bin目录下面) 其中没有用红色框起来的都是JVM自带的线程跟咱们没有关系。 其中t1,t2都是我们定义好的线程名称如果没有定义名字的话会是什么样子的呢 是这个样子的。 3.2. Thread 的常见属性 属性获取方法IDgetId()名称getName()状态getState()优先级getPriority()是否后台线程isDaemon()是否存活isAlive()是否被中断isInterrupted() ID 是线程的唯一标识不同线程不会重复名称是各种调试工具用到的状态表示线程当前所处的一个情况下面我们会进一步说明优先级高的线程理论上来说更容易被调度到关于后台线程需要记住JVM会在一个进程的所有非后台线程结束后才会结束运行是否存活一个简单的理解就是run方法是否运行结束了线程中断问题下面我们再说。 在这又得解释一些名词并且再加上一些代码。 第一个如果获取到线程的名字。 使用这个类中的函数 Thread.currentThread().getName() public class Demo07 {public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()-{while (true) {System.out.println(hello Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();while (true){System.out.println(hello main);Thread.sleep(1000);}} }第二个什么是isDaemon() 表格上写的是后台线程但是如果按照英语翻译的话是 守护线程。翻译成守护线程但是要把他理解成后台线程。怎么理解呢 具体例子中秋节亲戚们聚餐吃饭等到一家子人全部坐齐举杯碰一个之后然后开始吃饭但是么我下午还有课我就哐哐地吃饭我吃饱喝足要去上课了剩余的亲戚都没有吃完呢但是我就要出门了。 在上述的过程中把房间看成程序把自己一个人闷头吃饭看做一个线程(t1)把剩余的人一起吃完饭看成另一个线程(t2)对于t1来说不管t2是否完成t1一结束那么程序必须关闭那么此时就把t2设置为后台线程也就是说不管大家吃完了没有(t2是否结束)我吃完饭房间门必须要打开(程序就要结束了)我就要走了。 public static void main(String[] args) {Thread t1 new Thread(()-{while (true){System.out.println(全家人一直在吃);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 new Thread(()-{for (int i 0;i3;i){System.out.println(我也在吃但是吃完饭就要结束这个线程);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.setDaemon(true);t1.start();t2.start();}结果
如果没有setDemon(true)那会发生什么呢 public static void main(String[] args) {Thread t1 new Thread(()-{while (true){System.out.println(全家人一直在吃);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 new Thread(()-{for (int i 0;i3;i){System.out.println(我也在吃但是吃完饭就要结束这个线程);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//t1.setDaemon(true);t1.start();t2.start();}如果按照第二种的话意思就是说我都已经吃饱喝足了我还是不走线程 t2 还要在程序中等待。 获得 线程的名字 在上面的代码已经使用过了在这都不写了。 3.3. start() — 启动一个线程 public static void main(String[] args) {Thread t new Thread(() - {System.out.println(hello thread);});t.start();}这个是个很简单的代码大家应该都可以看懂如果我们这样写代码看看会出现什么情况 public static void main(String[] args) {Thread t new Thread(() - {System.out.println(hello thread);});t.start();t.start();}由此我们可以看出线程不能重复start。 通过我们查看 线程 start的。
3.4. 中断一个线程 我们使用两种方式来中断线程方案一使用变量 isFinished方案二使用interrupt方法。 方案一使用变量 isFinished。 public class Demo10 {public static boolean isFinshed false;public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {while (!isFinshed){System.out.println(Hello Thread!);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(3000);isFinshed true;} } 上面代码意思t线程每隔1秒打印一个 “Hello Thread!”3秒以后不再打印。 这个是通过 isFinished 变量来确定的。在这提一个问题 public static boolean isFinshed false;这个代码可不可以写在main函数里面? 答案是不行的。 public class Demo10 {//public static boolean isFinshed false;public static void main(String[] args) throws InterruptedException {boolean isFinshed false;Thread t new Thread(() - {while (!isFinshed){System.out.println(Hello Thread!);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(3000);isFinshed true;} }为什么呢 在第二种写法中(isFinished变量在main方法中)触发了变量捕获。 lambda是回调函数执行时机是很久以后才会被执行的(操作系统真正创建出线程之后)很有可能后续线程都创建好了当前main这里的方法都执行完了对应的isFinished 都已经销毁了为了解决这个问题Java的做法是把被捕获的变量拷贝一份拷贝到 lambda im外面的变量是否销毁并不影响 lambda方面里面的变量。 但是还是没有解决问题拷贝意味着这样的变量就不适合进行修改修改一方另外一方不会随之变化本质上是两个变量这种一边变化一边又不变可能会给程序员带来更多的疑惑那么Java大佬直接压根就不让这个变量进行修改这就是变量捕获操作。 那么为什么第一种都可以了 因为第一种写法把 isFinished 这个变量改写成 成员变量此时就不再是 “变量捕获” 语法 而是切换成 “内部类访问外部类的成员”语法。 lambda 本质上是 函数式接口相当于一个内部类isFinished 变量本身是外部类的成员内部类本来就能够访问外部类的成员。并且 成员变量的生命周期也是让 GC (垃圾回收)管理的在 lambda 里面不担心变量生命周期失效的问题那么就不必拷贝也就不必限制 final 之类。 下面我们使用 Interrupt 方法来终止线程。 public class Demo11 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {while (!Thread.currentThread().isInterrupted()){System.out.println(Hello Thread!);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(t 线程 结束);});t.start();Thread.sleep(3000);System.out.println(main 线程 尝试 阻塞 t 线程);t.interrupt();} } 结果 其中 Thread.currentThread().isInterrupted()判断线程时候被终止了 (其实是判断 Thread 里的 boolean 变量的值) t.interrput()主动去进行终止的 (修改这个 Boolean 变量的值) 当然除了设置Boolean 变量 (标志位) 之外还能够唤醒 像 sleep 这样的阻塞方法。 如果我们把 throw new RuntimeException(e); 这个代码给注释掉看看会发生什么情况。 public class Demo11 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {while (!Thread.currentThread().isInterrupted()){System.out.println(Hello Thread!);try {Thread.sleep(1000);} catch (InterruptedException e) {//throw new RuntimeException(e);break;}}System.out.println(t 线程 结束);});t.start();Thread.sleep(3000);System.out.println(main 线程 尝试 阻塞 t 线程);t.interrupt();} }结果 发生上面的代码的原因是 sleep 捣鬼。 正常来说调用 interrput 方法就会修改 isInterrputted 方法内部的标志位 设置为 true 由于上述代码是把 sleep 给 唤醒了 这种唤醒的情况下sleep 就会在唤醒 之后把 isInterruptted 标志位 设置为 false 因此在这样的情况下如果继续执行到循环的条件判定就会能够继续执行。 3.5. 等待线程 多个线程之间 是并发执行的也是随机调度的。 站在 程序员的角度来说咱们不喜欢随机的东西的。 join 能够要求多个线程之间结束的 先后顺序 sleep 可以通过 休眠的时间来控制线程结束的顺序的但是有的时候这样的设定是并不科学的。 有的时候希望 t 先 结束main 就可以紧跟着结束此时通过设置时间的方式不一定靠谱。 public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(()-{for (int i 0;i3;i){System.out.println(hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(t 线程结束);});t.start();t.join();System.out.println(main 线程结束);} }结果 此时上面代码的逻辑就是t每休息1s打印一次 hello thread执行三次main一直等待。 join 也提供了带有时间参数的版本。意思就是等待的最多时间 public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(()-{for (int i 0;i5;i){System.out.println(hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(t 线程结束);});t.start();t.join(3000);System.out.println(main 线程结束);} }此时t.joiin(3000)意思就是如果在 30000ms之内t 线程结束了那么此时 join 立即继续执行 (不会等满 3000ms)如果超过3000mst线程还没结束那么此时 join() 往下执行就不等 t 线程了。 这种带有超时时间版本的等待才是是更加科学的做法。计算机中尤其是和网络通信相关的逻辑一般都是需要超时时间的# 3.6. 休眠当前线程 这个sleep方法我们之前一直在用在这我们就不展开细讲了 下节课我们就要讲述线程的状态和线程不安全的情况了我们不见不散。