《Java并发编程的艺术》第四章——Java并发编程基础
来源:互联网 发布:java可变长参数 使用 编辑:程序博客网 时间:2024/05/17 03:45
知识点:
- 线程简介 。
- 启动和终止线程 。
- 线程间通信 。
- 线程应用实例。
1.线程简介
1.1 什么是线程?
要想明白什么是线程,必须先明白什么是进程!现在操作系统在运行一个程序时,会为其创建一个进程。而线程是操作系统调度的最小单元,也叫轻量级进程。一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈、和局部变量等属性,并能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程是在同时执行。
1.2 为什么要使用多线程?
- 更多的处理器核心:随着处理器上的核心数量越来越多,以及超线程技术的广泛应用,目前大多数计算机比以前更擅长并行计算,处理器性能的提升方式,也从更高的主频向更多的核心发展。如何利用好处理器上的多个核心也成了突出问题。 线程作为操作系统调度的最小单位,在一个时刻内只能运行在一个处理器核心上。如果不使用多线程,那么引入在多的处理器核心也对提升性能没有意义。
- 更快的响应时间: 有时候我们会编写一些较为复杂的代码,其中包含很多个操作。那么用户提交请求和需要等待所有操作全部执行完毕才可以。如果使用多线程,我们可以把不需要同步返回信息的操作交给其他线程处理,以缩短响应时间。
- 更好的编程模型:Java为多线程编程提供了良好、考究且一致的编程模型,使开发人员能够更加专注于问题的解决,即为所遇到的问题建立合适的模型,而不是绞尽脑汁的考虑如何将其多线程化。
1.3 线程优先级
现在操作系统基本采用分配时间片的方式调度运行的线程,线程会分配到若干时间片,当线程时间片用完后就会发生调度,等待下次分配。线程分配到的时间片多少决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要多或少分配处理器资源的线程属性。
在Java中,通过一个整型变量priority来控制优先级,优先级范围从1~10,在线程构建时可以使用setPriority(int)来修改优先级,默认为5,优先级高的线程分配时间片的数量要多于优先级低的线程。在不同的JVM及操作系统上,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。
【备注】:设置优先级时,针对频繁阻塞的线程需要设置较高的优先级,而偏重计算的线程则设置较低的优先级,确保处理器不会被独占。
1.4 线程的状态
Java线程在运行的生命周期中可能处于下表所示的6中不同的状态,在给定的一个时刻,线程只能处于其中一个状态:
实例:
package com.lipeng.fourth;import java.util.concurrent.TimeUnit;public class Demo { public static void main(String[] args) { final Object object1=new Object(); final Object object2=new Object(); Thread thread1=new Thread(new Runnable() { @Override public void run() { try { synchronized (object1) { TimeUnit.SECONDS.sleep(30); } } catch (InterruptedException e) { e.printStackTrace(); } } },"Demo-Thread-1"); Thread thread2=new Thread(new Runnable() { @Override public void run() { try { synchronized (object1) { object1.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"Demo-Thread-2"); Thread thread3=new Thread(new Runnable() { @Override public void run() { try { synchronized (object2) { object2.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"Demo-Thread-3"); Thread thread4=new Thread(new Runnable() { @Override public void run() { try { synchronized (object2) { object2.wait(30000); } } catch (InterruptedException e) { e.printStackTrace(); } } },"Demo-Thread-4"); thread1.start(); thread2.start(); thread3.start(); thread4.start(); }}
通过Java VisualVM查看线程栈信息:
线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间切换。其状态转换图如下:
【备注】:Java将操作系统中的运行和就绪两个状态合并称为运行状态。
1.5 守护(Daemon)线程
守护线程是一种支持型线程,主要用作程序中后台调度以及支持性工作(如JVM中负责GC的线程就是守护线程)。当JVM中不存在非守护线程时,JVM将退出。可以通过调用Thread.setDaemon(true)方法将线程设置为守护线程。
实例一
代码:
package com.lipeng.fourth;import java.util.concurrent.TimeUnit;public class DaemonDemo { public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { try { System.out.println("daemon thread start"); TimeUnit.SECONDS.sleep(3); } catch (Exception e) { }finally{ System.out.println("daemon thread done"); } } }); thread.setDaemon(true); thread.start(); }}
运行结果:
解析:main线程在启动守护线程之后main方法终止,目前JVM中无非Daemon线程,JVM退出。JVM中所有Daemon线程立即终止,所以finally块中的代码没有执行。
【备注】:在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。
实例二
代码:
package com.lipeng.fourth;public class DaemonDemo { public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { try { System.out.println("daemon thread start"); } catch (Exception e) { }finally{ System.out.println("daemon thread done"); } } }); thread.setDaemon(true); thread.start(); }}
运行结果:
解析:之所以会执行守护线程finally块中的代码,是因为在守护线程执行完毕之前,main线程还没有执行完,所以JVM不会退出。
2. 启动和终止线程
2.1 构造线程
在运行线程之前首先要构造一个线程对象,线程对象在构造时需要提供线程所需要的属性。如线程所属线程组、优先级、是否是Daemon线程等信息。下图为java.lang.Thread中对线程进行初始化的部分:
一个新构造的线程对象其parent线程进行空间分配,而child线程继承了parent是否是Daemon、优先级和加载资源的contextClassLoader及可继承的ThreadLocal,同时会分配一个唯一的ID来标识此child线程。至此,一个能够运行的线程对象初始化完毕,在堆内存中等待运行。
【备注】:在Java中,构造线程有三种方式:实现Runnable接口、实现Callable接口、继承Thread类。
2.2 启动线程
调用start()方法可启动线程。其含义为:当前线程(parent)同步告知JVM,只要线程规划器空闲,应立即启动调用start()方法的线程。
【备注】:直接调用run()方法和调用start()方法的区别:调用run方法,实质上只是以普通的方式调用方法,并不能使JVM去启动一个新线程。
2.3 中断
中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。线程通过检查自身是否被中断来进行响应,线程可通过isInterrupted()来进行判断是否被中断,也可调用Thread.interrupted()方法将中断标识位进行复位。如果该线程已经处于终结状态,即使被中断过,调用isInterrupted()时仍会返回false。
在Java中,许多声明抛出InterruptedException的方法在抛出异常之前,JVM会先将该线程的中断标识位清除,然后抛出异常,此时调用isInterrupted()时同样会返回false。
实例一
代码:
package com.lipeng.fourth;import java.util.concurrent.TimeUnit;public class InterruptDemo { public static void main(String[] args) { Thread thread1=new Thread(new Runnable() { @Override public void run() { while(true){ } } }); Thread thread2=new Thread(new Runnable() { @Override public void run() { while(true){ try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); thread1.start(); thread2.start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } thread1.interrupt(); thread2.interrupt(); System.out.println("thread 1 interrupt is "+thread1.isInterrupted()); System.out.println("thread 2 interrupt is "+thread2.isInterrupted()); }}
运行结果:
解析:线程2在抛出InterruptedException前,其中断标识位被清除,所以isInterrupted()方法返回false。
2.4 安全的终止线程
除了上面提到的使用中断来终止线程外,还可以利用一个boolean变量来作为条件控制线程是否终止。
实例一
package com.lipeng.fourth;import java.util.concurrent.TimeUnit;public class CancelThreadDemo { static volatile boolean flag=true; public static void main(String[] args) { WorkThread workThread=new WorkThread(); new Thread(workThread).start(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } flag=false; } static class WorkThread implements Runnable{ @Override public void run() { while(flag){ //do something System.out.println("do something"+System.currentTimeMillis()); } } }}
3. 线程间通信
- 《Java并发编程的艺术》第四章——Java并发编程基础
- 《java并发编程的艺术》读书笔记-第四章java并发的基础
- Java并发编程的艺术-第四章之Java并发基础
- Java并发编程的艺术
- Java并发编程的艺术
- Java并发编程艺术 4 Java并发编程基础
- 《Java并发编程的艺术》第二章——Java并发机制的底层实现
- java并发编程——并发基础
- 《Java并发编程的艺术》笔记五——Java线程基础.md
- Java并发编程的艺术——互动出版网
- Java并发编程的艺术——互动出版网
- Java并发编程的艺术(二)——重排序
- Java并发编程的艺术(三)——volatile
- Java并发编程的艺术(五)——中断
- Java并发编程的艺术(二)——重排序
- Java并发编程的艺术(三)——volatile
- Java并发编程的艺术(七)——Executors
- Java并发编程的艺术(十二)——线程安全
- android 6.0版本适配的几个重要方面
- java实现SP00LING假脱机输入输出技术模拟
- 数据透视表 笔记
- 哈尔滨理工大学第七届程序设计竞赛决赛(网络赛-高年级组)D 数圈圈【DFS||数位DP】
- Android踩坑日记:android7.0动态相机权限
- 《Java并发编程的艺术》第四章——Java并发编程基础
- ZooKeeper 原理及其在 Hadoop 和 HBase 中的应用
- 递归思想和实例
- linux wget 命令用法详解(附实例说明)
- 浅谈Java中的hashcode方法
- Python入门自检
- 深耕网站安全防护技术研究的智恒再获奖项
- hexo创建新博客后自动打开
- solr6.X 404 403 问题总结