「java多线程实战」java多线程编程实战
本篇文章给大家谈谈java多线程实战,以及java多线程编程实战对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
- 1、如何保证集合是线程安全的?
- 2、《Java多线程编程实战指南(核心篇)》epub下载在线阅读,求百度网盘云资源
- 3、java 程序中怎么保证多线程的运行安全?
- 4、java多线程下载 抛异常 出错在63行 为什么呢?求解 困扰几天了 新手不懂
- 5、如何创建线程?如何保证线程安全?
如何保证集合是线程安全的?
1、不可变
在java语言中,不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施。如final关键字修饰的数据不可修改,可靠性最高。
2、绝对线程安全
绝对的线程安全完全满足Brian GoetZ给出的线程安全的定义,这个定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”通常需要付出很大的代价。
3、相对线程安全
相对线程安全就是我们通常意义上所讲的一个类是“线程安全”的。
它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。
在java语言中,大部分的线程安全类都属于相对线程安全的,例如Vector、HashTable、Collections的synchronizedCollection()方法保证的集合。
4、线程兼容
线程兼容就是我们通常意义上所讲的一个类不是线程安全的。
线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境下可以安全地使用。Java API中大部分的类都是属于线程兼容的。如与前面的Vector和HashTable相对应的集合类ArrayList和HashMap等。
5、线程对立
线程对立是指无论调用端是否采取了同步错误,都无法在多线程环境中并发使用的代码。由于java语言天生就具有多线程特性,线程对立这种排斥多线程的代码是很少出现的。
一个线程对立的例子是Thread类的supend()和resume()方法。如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时是否进行了同步,目标线程都有死锁风险。正因此如此,这两个方法已经被废弃啦。
《Java多线程编程实战指南(核心篇)》epub下载在线阅读,求百度网盘云资源
《Java多线程编程实战指南(核心篇)》(黄文海)电子书网盘下载免费在线阅读
资源链接:
链接:
提取码:uqrv
书名:Java多线程编程实战指南(核心篇)
作者:黄文海
豆瓣评分:9.0
出版社:电子工业出版社
出版年份:2017-4
页数:480
内容简介:随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了——如今就连智能手机这样的消费类设备都已配备了4核乃至8核的处理器,更何况商用系统!在此背景下,以往靠单个处理器自身处理能力的提升所带来的软件计算性能提升的那种“免费午餐”已不复存在,这使得多线程编程在充分利用计算资源、提高软件服务质量方面扮演了越来越重要的角色。故而,掌握多线程编程技能对广大开发人员的重要性亦由此可见一斑。《Java多线程编程实战指南(核心篇)》以基本概念、原理与方法为主线,辅以丰富的实战案例和生活化实例,并从Java虚拟机、操作系统和硬件多个层次与角度出发,循序渐进、系统地介绍Java平台下的多线程编程核心技术及相关工具。
《Java多线程编程实战指南(核心篇)》适合有一定Java语言基础的读者作为入门多线程编程之用,也适合有一定多线程编程经验的读者作为重新梳理知识结构以提升认知层次和参考之用。
java 程序中怎么保证多线程的运行安全?
零基础学习java可按照这份大纲来进行学习
第一阶段:Java专业基础课程
阶段目标:
1. 熟练掌握Java的开发环境与编程核心知识
2. 熟练运用Java面向对象知识进行程序开发
3. 对Java的核心对象和组件有深入理解
4. 熟练应用JavaAPI相关知识
5. 熟练应用JAVA多线程技术
6. 能综合运用所学知识完成一个项目
知识点:
1、基本数据类型,运算符,数组,掌握基本数据类型转换,运算符,流程控制。
2、数组,排序算法,Java常用API,类和对象,了解类与对象,熟悉常用API。
3、面向对象特性,集合框架,熟悉面向对象三大特性,熟练使用集合框架。
4、IO流,多线程。
5、网络协议,线程运用。
第二阶段:JavaWEB核心课程
阶段目标:
1. 熟练掌握数据库和MySQL核心技术
2. 深入理解JDBC与DAO数据库操作
3. 熟练运用JSP及Servlet技术完成网站后台开发
4. 深入理解缓存,连接池,注解,反射,泛型等知识
5. 能够运用所学知识完成自定义框架
知识点:
1、数据库知识,范式,MySQL配置,命令,建库建表,数据的增删改查,约束,视图,存储过程,函数,触发器,事务,游标,建模工具。
2、深入理解数据库管理系统通用知识及MySQL数据库的使用与管理。为Java后台开发打下坚实基础。Web页面元素,布局,CSS样式,盒模型,JavaScript,jQuery。
3、掌握前端开发技术,掌握jQuery。
4、Servlet,EL表达式,会话跟踪技术,过滤器,FreeMarker。
5、掌握Servlet相关技术,利用Servlet,JSP相关应用技术和DAO完成B/S架构下的应用开发。
6、泛型,反射,注解。
7、掌握JAVA高级应用,利用泛型,注解,枚举完成自己的CRUD框架开发为后续框架学习做铺垫。
8、单点登录,支付功能,项目整合,分页封装熟练运用JSP及Servlet核心知识完成项目实战。
第三阶段:JavaEE框架课程
阶段目标:
1. 熟练运用Linux操作系统常见命令及完成环境部署和Nginx服务器的配置
2. 熟练运用JavaEE三大核心框架:Spring,SpringMVC,MyBatis
3. 熟练运用Maven,并使用SpringBoot进行快速框架搭建
4. 深入理解框架的实现原理,Java底层技术,企业级应用等
5. 使用Shiro,Ztree和Spring,SpringMVC,Mybaits完成企业项目
知识点:
1、Linux安装配置,文件目录操作,VI命令,管理,用户与权限,环境部署,Struts2概述,hiberante概述。
2、Linux作为一个主流的服务器操作系统,是每一个开发工程师必须掌握的重点技术,并且能够熟练运用。
3、SSH的整合,MyBatis,SpringMVC,Maven的使用。
4、了解AOP原理,了解中央控制器原理,掌握MyBatis框架,掌握SSM框架的整合。
5、Shiro,Ztree,项目文档,项目规范,需求分析,原型图设计,数据库设计,工程构建,需求评审,配置管理,BUG修复,项目管理等。
6、独立自主完成一个中小型的企业级综合项目的设计和整体架构的原型和建模。独立自主完成一个大型的企业级综合项目,并具备商业价值
java多线程下载 抛异常 出错在63行 为什么呢?求解 困扰几天了 新手不懂
给你个多线程下载文件的例子你看下。
/**
* 文件:ThreadDownloadFile.java
* 描述:TODO
* 作者:EX-QINCIDONG001
* 日期:2012-2-22
*/
package com.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author EX-QINCIDONG001 多线程下载文件。 思想:开启5个线程,每个线程下载文件大小的1/5,br
* 每个线程生成的文件以文件名_编号作为文件名。br
* 每个线程结束后都检查自己是否是最后一个线程。br
* 如果是最后一个线程,就启动合并文件的线程br
* 用RandomAccessFile类,有追加文件的方法。br
* 扫描所有的文件,合并为一个文件。
*
* 例:br
* Thread File Name File Size
* Thread-1 test_1.mp3 300kb
* Thread-2 test_2.mp3 300kb
* Thread-3 test_3.mp3 200kb
*
* 最终的文件:test.mp3
*/
public class ThreadDownloadFile {
// 要下载的文件的URL
private String fileUrl;
// 要保存的文件名
private String saveName;
// 要创建的线程数量
private static final int THREAD_COUNT = 5;
// 保存线程运行的状态(0标识线程还在运行,1标识结束)
MapInteger, Integer threadStatusMap = new HashMapInteger, Integer();
public ThreadDownloadFile(String fileUrl,String saveName) {
this.fileUrl = fileUrl;
this.saveName = saveName;
// 初始化线程运行状态
for (int i=0;iTHREAD_COUNT;i++) {
// key:线程编号,value:线程运行状态
threadStatusMap.put(i, 0);
}
}
private void download() throws IOException {
URL url = new URL(this.fileUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setAllowUserInteraction(true);
conn.connect();
// 文件总的长度
int contentLength = conn.getContentLength();
// 每个线程应该分配的长度
int partLength = contentLength / this.THREAD_COUNT + 1;
conn.disconnect();
System.out.println("开始下载文件...");
for (int i = 0; i this.THREAD_COUNT; i++) {
int length = partLength;
if (i == this.THREAD_COUNT - 1) { // 最后一个的长度
length = contentLength - partLength * i;
}
int index1 = saveName.lastIndexOf("/");
int index2 = saveName.lastIndexOf(".");
String partFileName = saveName.substring(0, index1+1)
+ saveName.substring(index1+1, index2) + "_"+(i + 1)
+ saveName.substring(index2, saveName.length());
DownloadThread dt = new DownloadThread(conn,contentLength,this.THREAD_COUNT,i,length,i*length, url, partFileName,threadStatusMap,this.saveName);
dt.start();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// String saveName = "";
// int index1 = saveName.lastIndexOf("/");
// int index2 = saveName.lastIndexOf(".");
// int i = 0;
// String partFileName = saveName.substring(0, index1+1)
// + saveName.substring(index1+1, index2) +"_"+ (i + 1)
// + saveName.substring(index2, saveName.length());
// System.out.println(partFileName);
String fileUrl = "";
String saveName = "d:/test/ajax实战.pdf";
ThreadDownloadFile tdf = new ThreadDownloadFile(fileUrl,saveName);
try {
tdf.download();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class DownloadThread extends Thread {
// 总长度
private int contentLength;
// 线程数
private int threadNum;
// 当前线程编号
private int currentThreadNum;
private int length;
private int offset;
private URL url;
private String partFileName;
// 要保存的文件名
private String saveName;
private MapInteger,Integer threadStatusMap;
HttpURLConnection conn;
DownloadThread(HttpURLConnection conn,int contentLength,int threadNum,int currentThreadNum,int length,int offset, URL url, String partFileName,MapInteger,Integer threadStatusMap,String saveName) {
this.conn = conn;
this.contentLength = contentLength;
this.threadNum = threadNum;
this.currentThreadNum = currentThreadNum;
this.length = length;
this.offset = offset;
this.url = url;
this.partFileName = partFileName;
this.threadStatusMap = threadStatusMap;
this.saveName = saveName;
}
public void run() {
System.out.println("线程【" +this.currentThreadNum + "】开启...");
// HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setAllowUserInteraction(true);
conn.setRequestProperty("RANGE", "bytes=" + offset );
conn.connect();
// 设置断点续传的开始位置
FileOutputStream fos = new FileOutputStream(partFileName);
InputStream is = conn.getInputStream();
byte[] data = new byte[1024];
int len = -1;
int readSize = 0;
while ((len = is.read(data)) != -1) {
readSize += len;
if (readSize length) { // 只读取length长度
len = len - (readSize - length);
}
fos.write(data, 0, len);
if (readSize length) {
break;
}
}
fos.flush();
is.close();
// 将线程运行状态改为1(结束)
this.threadStatusMap.remove(currentThreadNum);
this.threadStatusMap.put(currentThreadNum, 1);
// 检查是否全部想线程都运行完毕
boolean flag = true;
SetInteger keys = this.threadStatusMap.keySet();
IteratorInteger its = keys.iterator();
while (its.hasNext()) {
Integer key = its.next();
Integer value = this.threadStatusMap.get(key);
if (value == 0) {
flag = false;
break;
}
}
System.out.println("线程【" +this.currentThreadNum + "】结束...");
if (flag) { // 所有的下载线程均结束,可以开始合并文件的线程
MergeThread mt = new MergeThread(this.contentLength,this.threadNum,this.saveName);
mt.start();
System.out.println("文件下载完毕...\n文件保存位置:" + this.saveName);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}
/**
* 合并文件的线程。
* @author EX-QINCIDONG001
*
*/
class MergeThread extends Thread {
// 文件总长度
private int contentLength;
// 线程的数量,以便于知道,生成了几个子文件。
private int threadNum;
// 要保存的最终的文件名
private String saveName;
public MergeThread(int contentLength,int threadNum,String saveName) {
this.contentLength = contentLength;
this.threadNum = threadNum;
this.saveName = saveName;
}
public void run() {
int index1 = saveName.lastIndexOf("/");
int index2 = saveName.lastIndexOf(".");
RandomAccessFile accessFile = null;
try {
accessFile = new RandomAccessFile(saveName,"rw");
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 每个线程应该分配的长度
int partLength = contentLength / this.threadNum + 1;
for (int i=0;ithreadNum;i++) {
// 子文件文件名
String partFileName = saveName.substring(0, index1+1)
+ saveName.substring(index1+1, index2) +"_"+ (i + 1)
+ saveName.substring(index2, saveName.length());
int length = partLength;
if (i == this.threadNum - 1) { // 最后一个的长度
length = contentLength - partLength * i;
}
try {
accessFile.seek(i*length);
FileInputStream fis = new FileInputStream(partFileName);
byte[] data = new byte[1024];
int len = -1;
while ((len = fis.read(data)) != -1) {
accessFile.write(data,0,len);
}
fis.close();
accessFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
如何创建线程?如何保证线程安全?
线程安全等级
之前的博客中已有所提及“线程安全”问题,一般我们常说某某类是线程安全的,某某是非线程安全的。其实线程安全并不是一个“非黑即白”单项选择题。按照“线程安全”的安全程度由强到弱来排序,我们可以将java语言中各种操作共享的数据分为以下5类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。
1、不可变
在java语言中,不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施。如final关键字修饰的数据不可修改,可靠性最高。
2、绝对线程安全
绝对的线程安全完全满足Brian GoetZ给出的线程安全的定义,这个定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”通常需要付出很大的代价。
3、相对线程安全
相对线程安全就是我们通常意义上所讲的一个类是“线程安全”的。
它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。
在java语言中,大部分的线程安全类都属于相对线程安全的,例如Vector、HashTable、Collections的synchronizedCollection()方法保证的集合。
4、线程兼容
线程兼容就是我们通常意义上所讲的一个类不是线程安全的。
线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境下可以安全地使用。Java API中大部分的类都是属于线程兼容的。如与前面的Vector和HashTable相对应的集合类ArrayList和HashMap等。
5、线程对立
线程对立是指无论调用端是否采取了同步错误,都无法在多线程环境中并发使用的代码。由于java语言天生就具有多线程特性,线程对立这种排斥多线程的代码是很少出现的。
一个线程对立的例子是Thread类的supend()和resume()方法。如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时是否进行了同步,目标线程都有死锁风险。正因此如此,这两个方法已经被废弃啦。
二、线程安全的实现方法
保证线程安全以是否需要同步手段分类,分为同步方案和无需同步方案。
1、互斥同步
互斥同步是最常见的一种并发正确性保障手段。同步是指在多线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用(同一时刻,只有一个线程在操作共享数据)。而互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。
在java中,最基本的互斥同步手段就是synchronized关键字,synchronized关键字编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码质量,这两个字节码指令都需要一个reference类型的参数来指明要锁定和解锁的对象。
此外,ReentrantLock也是通过互斥来实现同步。在基本用法上,ReentrantLock与synchronized很相似,他们都具备一样的线程重入特性。
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也成为阻塞同步。从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确地同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁。
2、非阻塞同步
随着硬件指令集的发展,出现了基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采用其他的补偿措施。(最常见的补偿错误就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。
非阻塞的实现CAS(compareandswap):CAS指令需要有3个操作数,分别是内存地址(在java中理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,CAS指令指令时,当且仅当V处的值符合旧预期值A时,处理器用B更新V处的值,否则它就不执行更新,但是无论是否更新了V处的值,都会返回V的旧值,上述的处理过程是一个原子操作。
CAS缺点:
ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加版本号,每次变量更新的时候把版本号加一,那么A-B-A就变成了1A-2B-3C。JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
3、无需同步方案
要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无需任何同步操作去保证正确性,因此会有一些代码天生就是线程安全的。
1)可重入代码
可重入代码(ReentrantCode)也称为纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误。所有的可重入代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。
可重入代码的特点是不依赖存储在堆上的数据和公用的系统资源、用到的状态量都是由参数中传入、不调用 非可重入的方法等。
(类比:synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时时可以再次得到该对象的锁)
2)线程本地存储
如果一段代码中所需的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内。这样无需同步也能保证线程之间不出现数据的争用问题。
符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完。其中最重要的一个应用实例就是经典的Web交互模型中的“一个请求对应一个服务器线程(Thread-per-Request)”的处理方式,这种处理方式的广泛应用使得很多Web服务器应用都可以使用线程本地存储来解决线程安全问题。
java多线程实战的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java多线程编程实战、java多线程实战的信息别忘了在本站进行查找喔。
发布于:2022-11-29,除非注明,否则均为
原创文章,转载请注明出处。