Java并发编程1

1 为什么要使用多线程

使用多线程的原因主要有以下几点:

(1)更有效的运用多核心处理器

一个线程在一个时刻只能运行在一个处理器核心上,所以单线程程序在同一时刻只能使用一个处理器核心,而多个线程在同一时刻可以使用多个处理器核心,显然能更加有效的运用多核心处理器。

(2)更快的响应时间

一些较为复杂的代码可以使用多线程技术,即将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列),如生成订单快照、发送邮件等。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短了响应时间,提升了用户体验。

(3)更好的编程模型

Java为多线程编程提供了良好、考究并且一致的编程模型,使开发人员能够更加专注于问题的解决,即为所遇到的问题建立合适的模型,而不是绞尽脑汁地考虑如何将其多线程化。一旦开发人员建立好了模型,稍做修改总是能够方便地映射到Java提供的多线程编程模型上。

2 线程优先级

(1)通过一个整型成员变量priority来控制优先级,优先级高的线程分配时间片的数量要多于优先级低的线程。

(2)可以通过setPriority(int)方法来修改优先级,优先级的范围从1~10,默认优先级是5。

(3)设置线程优先级时,针对频繁阻塞(休眠或者I/O操作)的线程需要设置较高优先级,而偏重计算(需要较多CPU时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。

(4)线程优先级不能作为程序正确性的依赖,有以下两点原因:1 在数量基数不大的情况下优先级的区分并不明显,甚至出现优先级低的线程先于优先级高的执行的情况。2 有些操作系统会忽略Java对线程优先级的设定,例如:Mac OS X 10.10,Java版本为1.7.0_71,环境下和Ubuntu 14.04环境下,所有Java线程优先级均为5,对线程优先级的设置会被忽略。

3 线程的状态

Java线程在运行的生命周期中可能处于下表所示的6种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。

Java线程状态变迁如下图示:

注意:(1)Java将操作系统中的运行和就绪两个状态合并称为运行状态。(2)阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,但是阻塞在java.concurrent包中Lock接口的线程状态却是等待状态,因为java.concurrent包中Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。

4 Daemon线程

(1)Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。

(2)当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。

(3)可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。

注意: Daemon属性需要在启动线程之前设置,不能在启动线程之后设置。

(4)Daemon线程被用作完成支持性工作,但是在Java虚拟机退出时Daemon线程中的finally块
并不一定会执行,所以不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑,示例如下:

1public class Daemon { 2 public static void main(String[] args) { 3 Thread thread = new Thread(new DaemonRunner(), "DaemonRunner"); 4 thread.setDaemon(true); 5 thread.start(); 6 } 7 8 static class DaemonRunner implements Runnable { 9 @Override 10 public void run() { 11 try { 12 SleepUtils.second(10); 13 } finally { 14 System.out.println("DaemonThread finally run."); 15 } 16 } 17 } 18}

运行Daemon程序,可以看到在终端或者命令提示符上没有任何输出。main线程(非Daemon线程)在启动了线程DaemonRunner之后随着main方法执行完毕而终止,而此时Java虚拟机中已经没有非Daemon线程,虚拟机需要退出。Java虚拟机中的所有Daemon线程都需要立即终止,因此DaemonRunner立即终止,但是DaemonRunner中的finally块并没有执行。

5 构造线程

一个新构造的线程对象是由其parent线程来进行空间分配的,而child线程继承了parent是否为Daemon、优先级和加载资源的contextClassLoader以及可继承的ThreadLocal,同时还会分配一个唯一的ID来标识这个child线程。至此,一个能够运行的线程对象就初始化好了,在堆内存中等待着运行。java.lang.Thread中对线程进行初始化的部分的代码如下:

1private void init(ThreadGroup g, Runnable target, String name, 2 long stackSize, AccessControlContext acc) { 3 if (name == null) { 4 throw new NullPointerException("name cannot be null"); 5 } 6 // 当前线程就是该线程的父线程 7 Thread parent = currentThread(); 8 this.group = g; 9 // 将daemon、priority属性设置为父线程的对应属性 10 this.daemon = parent.isDaemon(); 11 this.priority = parent.getPriority(); 12 this.name = name.toCharArray(); 13 this.target = target; 14 setPriority(priority); 15 // 将父线程的InheritableThreadLocal复制过来 16 if (parent.inheritableThreadLocals != null) 17 this.inheritableThreadLocals = ThreadLocal 18 .createInheritedMap(parent.inheritableThreadLocals); 19 // 分配一个线程ID 20 tid = nextThreadID(); 21}

6 启动线程

线程对象在初始化完成之后,调用start()方法就可以启动这个线程。线程start()方法的含义是:当前线程(即parent线程)同步告知Java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。

注意:启动一个线程前,最好为这个线程设置线程名称,因为这样在使用jstack分析程
序或者进行问题排查时,就会给开发人员提供一些提示,自定义的线程最好能够起个名字。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )