当前位置:文档之家› 基于Java多线程的下载器源码剖析

基于Java多线程的下载器源码剖析

基于Java多线程的下载器源码剖析
基于Java多线程的下载器源码剖析

本文实现了一个基于Java多线程的下载器,可提供的功能有:

1. 对文件使用多线程下载,并显示每时刻的下载速度。

2. 对多个下载进行管理,包括线程调度,内存管理等。

这篇文章的结构如下:首先讨论如何实现利用Java多线程对单个文件进行下载。然后讨论当系统中有多个文件下载,如何对这些下载进行管理。包括线程调度,内存管理等。

一:单个文件下载的管理

1. 单文件下载类层次

首先简要介绍一下单个文件下载管理的类层次:

来一张图来表示。

?为需要下载的文件创建一个Download类,Download负责管理该文件下载时的线程管理、文件管理、当前速度计算等操作。

?根据线程的数目tNum,将该文件分为tNum段,每段为一个DownloadBlock。

在实际下载的过程中,并不是一次把所有的东西下载完,而是每次下载固定size的一段Di。所以每个DownloadBlock又会分成n段。

?为每个DownloadBlock申请一个线程DownloadThread。其主要作用就是每次下载一段Di,并将其写入到文件中。

2. 单文件下载

对于单个下载,步骤如下

?连接资源服务器,获取资源信息,创建文件

?切分资源,为每个线程分配固定的下载区域。

1)封装下载的属性

在建立下载之前,我们把每一次下载进行抽象封装。

首先把URL、目标文件等封装在一个DownloadConfig类中。

其中包含了4个属性:

[java]view plaincopy

?private URL url; //文件下载地址

?private File file; //下载文件保存目标文件

?private int nthread; //下载该文件需要的线程数

?private int priority; //该下载的优先级

如下如所示:

2)连接资源服务器,获取资源信息,创建文件,并指定文件大小

[java]view plaincopy

?length = config.getUrl().openConnection().getContentLength();

?RandomAccessFile file = new RandomAccessFile(config.getFile(), "rw");

?file.setLength(length);

?file.close();

3)切分资源,为每个线程分配固定的下载区域,并将当前的下载加入到队列中[java]view plaincopy

?int size = length / config.getNthread();

?for(int i = 0; i < config.getNthread(); i++){

?int start = i * size;

?int len;

?if(i == config.getNthread() - 1)

? len = length - start;

?else len = size;

?//并将当前的下载加入到下载队列中

? addDownloadBlock(getDownloadBlock(start, len));

?}

3)启动线程进行下载

下载的步骤如下:

1. 创建缓存,创建连接。设置获取资源数据的范围,创建文件,并设置写入位置[java]view plaincopy

?//创建缓存

?byte [] b;

?if(block.getLength() < Constants.BYTES_READ)

? b = new byte[(int)block.getLength()];

?else

? b = new byte[Constants.BYTES_READ];

?

?//创建连接。设置获取资源数据的范围,从startPos到endPos

?URLConnection con = null;

?con.setRequestProperty("Range", "bytes=" + block.getStart() + "-" + block.getSta rt()+block.getLength()-1);

?RandomAccessFile file = new RandomAccessFile(block.getDownload().getConfig().get File(), "rw");//创建RandomAccessFile

?file.seek(block.getStart()); //从startPos开始写入

2. 如果当前block的length大于0,则从URL资源处获取固定大小的资源,并将其写入到文件中。

3 .更新block块的start,以及length,如果length大于0,继续进行2,否则则表示当前block已经下载完毕,退出该线程。

[java]view plaincopy

?InputStream in = block.getDownload().getConfig().getUrl().openStream();

?int n;

?

?//对该block内的文件进行下载,

?while(count < block.getLength()){

?if (needSplit()) { // 检查该Block是否还需要分块(即当前block剩余的大小大于一次下

载的量)

?long newLength = (block.getLength() - count) / 2;

?long newStart = block.getStart() + block.getLength() - newLength;

? DownloadBlock newBlock = block.getDownload().getDownloadBlock(newStart, newLength);

? block.setLength(block.getLength() - newLength);

? block.getDownload().addDownloadBlock(newBlock);

? }

?

?

?//写入文件

? n = in.read(b);

?if(n < 0){

?break;

? }else if(count + n > block.getLength()){

? file.write(b, 0, (int)(block.getLength() - count));

? count = block.getLength();

? }else {

? count += n;

? file.write(b, 0, n);

? }

?

?// set block count in download

?if(n > 0){

?//统计每个block中已经下载的段的个数,用于计算当前下载的速度。

? block.getDownload().setBlockCount(block.getStart(), count);

? }

?}

?

?in.close();

?file.close();

二 . 当前文件下载速度与进度计算

如第一个图所表示的,每个Block中又分为了很多的段D1、D2、…Dn,因此当为了计算当前下载的速度,需要将下载的段D的数量统计出来,这里使用了一个ConcurrentHashMap来保存每个block已经下载完成的段D的数目。其中key为每个block的start值,而value为该block已经下载完的段D。

在当前时刻,我们需要统计当前Download已经下载完成段D的数量,然后再和上一时刻的相比较,则可以得出当前的下载速度。具体代码见下:[java]view plaincopy

?class CheckSpeedTask extends TimerTask{

?

?private static final Log log = LogFactory.getLog(CheckSpeedTask.class);

?private Download download;

?private ConcurrentHashMap blockCounts;

?

?private long speed = 0; // Byte/S

?private long count = 0; // Total downloaded byte count

?private long lastCount = 0;

?private long time = 0; // Check time

?private long lastTime = 0;

?

?public CheckSpeedTask(Download download, long startTime, ConcurrentHashMap blockCounts){

?this.download = download;

?https://www.doczj.com/doc/4f4382644.html,stTime = startTime;

?this.blockCounts = blockCounts;

? }

?

?@Override

?public void run() {

?try {

? time = System.currentTimeMillis();

? count = 0;

?//需要统计当前已经下载完成段D的数量。

?for(long c : blockCounts.values()){

? count += c;

? }

? speed = (count -lastCount)/((time - lastTime)/1000);

? log.debug(blockCounts.size() + " threads are downloading " + downloa

d + ", cuttent is " + speed + "Byte/S, " + (count * 1.0)/download.getLength()*10

0 + "% downloaded");

? download.setCount(count);

? download.setSpeed(speed);

? lastTime = time;

? lastCount = count;

? } catch (Exception e) {

?// TODO: handle exception

? e.printStackTrace();

? }

?

? }

?}

这样我们就可以在Thread类的run()函数中,计算当前下载的速度

[java]view plaincopy

?while(activeThreads.size() > 0 || blockQueue.size() > 0){

? Thread.sleep(1000);

? checkSpeed();

上面的代码演示了如何使用Java多线程对单个文件进行下载,接下来我们继续讨论如何对多个下载进行调度、管理等

三:多个文件下载的管理

这一节我们主要来讲一下如何对多个文件的下载进行管理

首先来看一下整个系统的UML图

从最下面开始说起:

Download代表一个下载类,对每一个文件都需要创建一个Download实例,用于对该文件下载线程的管理。其中每个Download中都有以下几个对象:

[java]view plaincopy

?private ConcurrentLinkedQueue blockQueue;

?private ConcurrentLinkedQueue blockCache;

?private ConcurrentHashMap blockCounts;

?private ConcurrentLinkedQueue activeThreads;

其中

?blockQueue是一个队列,用于存储当前需要下载的DownloadBlock。

Download对文件进行切分形成的DownloadBlock会被放入到放入到

blockQueue中,供以后的下载。

?blockCache为block内存缓存池,主要是为了能够复用已经建立好的DownloadBlock。

?blockCounts为一个Map,其中key为每个block的start值,而value为该block 已经下载完的段D。主要作用是统计出当前已经每个Block已经下载完的段D,以计算实时下载速度

?activeThreads 主要是为了存储该Thread中所有的活跃线程。DownloadBlock是一个下载块,其里面有3个成员变量

[java]view plaincopy

?private Download download; //其所属的Download

?private long start; //下载文件起始处

?private long length; //下载文件的长度

DownloadThread是指下载进程,每个DownloadBlock都需要启动一个DownloadThread去进行下载。即

[java]view plaincopy

?new DownloadThread(block).start()

DownloadDeamon为了一个守护线程。其内部主要为了下载所有的需要下载DownloadBlock

[java]view plaincopy

?private DownloadList downloads; //当前系统中所有的下载列表

?private ExecutorService threadPool; //线程池

Downloader 代表整个下载系统,整个系统中只有一个实例对象,因此我们需要保证系统中只有一个实例对象。

[java]view plaincopy

?private DownloaderConfig config; // Downloader配置

?private DownloadList downloads; //当前系统所有的下载列表private Thread deamon; //守护进程

?private ConcurrentLinkedQueue blockCache; //当前系统的缓存

?private Timer timer; //

看了上面一大堆的东西,我保证你现在很晕,OK,我们从使用的角度来看整个系统是如何运行的。

下面是示例代码。

[java]view plaincopy

?public static void main(String[] args) {

?

? Downloader downloader = Downloader.getInstance();

?

?//下载第一个文件

? String url1 = "https://https://www.doczj.com/doc/4f4382644.html,/files/tmsvm_src_v1.1.0.rar";

? String saveFile1 = "data/tmsvm_src_v1.1.0.rar";

? DownloadConfig config = new DownloadConfig();

?try {

? config.setUrl(new URL(url1));

? config.setFile(new File(saveFile1));

? config.setNthread(new Integer(5));

? config.setPriority(new Integer(6));

?//将第一个下载加入到下载列表中

? downloader.addDownload(new Download(config, downloader.getTimer()));

? } catch (MalformedURLException e) {

?// TODO Auto-generated catch block

? e.printStackTrace();

? }

?

?//下载第二个文件

? String url2 = "https://https://www.doczj.com/doc/4f4382644.html,/files/Tmsvm%E5%8F%82%E8%80%83%E6 %96%87%E6%A1%A3%28v1.1.0%29.rar";

? String saveFile2 = "data/Tmsvm参考文档(v1.1.0).rar";

?try {

? config.setUrl(new URL(url2));

? config.setFile(new File(saveFile2));

? config.setNthread(new Integer(5));

? config.setPriority(new Integer(6));

?//将第二个下载加入到下载列表中

? downloader.addDownload(new Download(config, downloader.getTimer()));

? } catch (MalformedURLException e) {

?// TODO Auto-generated catch block

? e.printStackTrace();

? }

1. 系统初始化

首先来看这一行行:

[java]view plaincopy

?Downloader downloader = Downloader.getInstance();

Downloader是这个下载器的总调度师,一山不容二虎,当然在系统运行过程中,只能有一个Downloader的实例,因此我们需要用单例模式来保证这一点。

首先要取得downloader实例,即系统的初始化。我们看系统初始化需要做什么?[java]view plaincopy

?public static Downloader getInstance(){

?if(downloader == null)

? downloader = new Downloader(new DownloaderConfig());

?return downloader;

?}

?

?private Downloader(DownloaderConfig config) {

?super();

?this.config = config;

? start();

?}

上面的代码中的start()函数中到底做了什么呢?

?初始化blockCache缓存,其中blockCache为

ConcurrentLinkedQueue类型。

?启动守护进程DownloadDeamon

具体代码如下:

[java]view plaincopy

?private void start(){

?blockCache = new ConcurrentLinkedQueue(); //初始化缓存

?downloads = new DownloadList();

?deamon = new Thread(new DownloadDeamon(downloads)); //初始化守护进程

?deamon.setDaemon(true);

?deamon.start();

?timer = new Timer(Constants.TIMER_NAME, true);

?}

上面代码中启动了一个守护进程。那么这个守护进程在启动的时候在做什么事情呢?

我们来看一下他的run()函数

[java]view plaincopy

?public void run() {

?System.out.println("Create thread pool");

?threadPool = Executors.newCachedThreadPool(); //初始化线程池

?DownloadBlock block;

?while(true){

?block = getDownloadBlock(); //不断从当前系统中获取待下载的DownloadBlock

?if(block != null){

?https://www.doczj.com/doc/4f4382644.html,("Create new download thread for " + block);

?//启动线程执行下载

?threadPool.execute(new DownloadThread(block));

?//将当前Block从其所在的Download中移除

?block.getDownload().removeDownloadBlock(block);

?}

?

?try {

? Thread.sleep(1000);

?} catch (InterruptedException e) {

? // TODO Auto-generated catch bloc k

? System.out.println("Download deam on stoped by user");

? break;

?}

?}

?}

守护进程所做的事情就是不断获取将要进行下载的Block,然后启动线程去进行下载。

来看一下获取Block的策略:这里不断的从当前下载列表中获取所有的Download,然后从里面选取最需要下载的文件,“最需要下载”定义为剩余的待下载量最多。其具体的代码看下方:

[java]view plaincopy

?private DownloadBlock getDownloadBlock(){

?downs= downloads.toArray();

?if(downs== null || downs.length == 0)

?return null;

?Downloaddownload = downs[0];

?for(Downloaddown : downs){//找最需要下载的Download进行下

载 if(down.getRemainThread()> download.getRemainThread())

?download= down;

?}

?download.descRemainThread();

?returndownload.getDownloadBlock();

?}

2. 新建下载

上面讲解的是系统初始化所做的事情。那么当我们把开始下载一个文件时系统是怎么运行的呢?

[java]view plaincopy

?//下载第一个文件

?String url1 = "https://https://www.doczj.com/doc/4f4382644.html,/files/tmsvm_src_v1.1.0.rar";

?String saveFile1 = "data/tmsvm_src_v1.1.0.rar";

?DownloadConfig config = new DownloadConfig();

?try {

? config.setUrl(new URL(url1));

? config.setFile(new File(saveFile1));

? config.setNthread(new Integer(5));

? config.setPriority(new Integer(6));

?//将第一个下载加入到下载列表中

? downloader.addDownload(new Download(config, downloader.getTimer()));

?} catch (MalformedURLException e) {

?// TODO Auto-generated catch block

? e.printStackTrace();

?}

我们重点来看这一句:

[java]view plaincopy

?//将第一个下载加入到下载列表中

?downloader.addDownload(new Download(config, downloader.getTimer())); addDownload的定义如下

[java]view plaincopy

?public boolean addDownload(Download download){

?new Thread(download).start();

?return downloads.add(download);

?}

这段代码做了两件事情:

?为Download启动一下线程。Download线程所做的事情就是把当前的文件根据线程数目进行切分。

?把当前Download加入到DownloadList中。

OK,我们看看,Download是切分文件时是如何与整个系统联系在一起。

[java]view plaincopy

?public void run(){

?try {

? begin = System.currentTimeMillis();

?

?// get length

? https://www.doczj.com/doc/4f4382644.html,("Begin download " + config.getUrl());

? length = config.getUrl().openConnection().getContentLength();

? https://www.doczj.com/doc/4f4382644.html,("Total size : " + length);

?

?// create file

? https://www.doczj.com/doc/4f4382644.html,("Create file " + config.getFile());

? RandomAccessFile file = new RandomAccessFile(config.getFile(), "rw");

? file.setLength(length);

? https://www.doczj.com/doc/4f4382644.html,("Created with length = " + length);

? file.close();

?

?int size = length / config.getNthread();

?

?// add initial blocks

? log.debug("Add initial " + config.getNthread() + " download blocks");

?for(int i = 0; i < config.getNthread(); i++){

?int start = i * size;

?int len;

?if(i == config.getNthread() - 1)

? len = length - start;

?else len = size;

? addDownloadBlock(getDownloadBlock(start, len));

? }

?

?// set task that checks speed every 1 second

? log.debug("Set task for speed check");

? checkSpeedTask = new CheckSpeedTask(this, System.currentTimeMillis()-10, blockCounts);

?//timer.schedule(checkSpeedTask, 1000, 1000);

?// set task that creates new blocks every 1 minute

? log.debug("Set task for split blocks");

? splitBlockTask = new SplitBlockTask(this, System.currentTimeMillis()-10, blockCounts, activeThreads);

? timer.schedule(splitBlockTask, 60*1000, 60*1000);

?

?// wait for all blocks complete

? log.debug("Waiting for all blocks to complete");

?while(activeThreads.size() > 0 || blockQueue.size() > 0){

? Thread.sleep(1000);

? checkSpeed();

? }

?

?// stop the tasks

? checkSpeedTask.cancel();

? splitBlockTask.cancel();

?

?long total = System.currentTimeMillis() - begin;

? speed = length/(total/1000);

? https://www.doczj.com/doc/4f4382644.html,("Complete download " + config.getUrl() + "\n"

? + "Total time : " + total + " ms" + "\n"

? + "Average speed: " + speed + "Byte/s"

? );

?

? log.debug(this + " put all block in blockCache back to downloader system ");

?for(DownloadBlock block : blockCache){

? Downloader.getInstance().putDownloadBlock(block);

? }

?

? } catch (FileNotFoundException e) {

?// TODO Auto-generated catch block

? e.printStackTrace();

? } catch (IOException e) {

?// TODO Auto-generated catch block

? e.printStackTrace();

? } catch (InterruptedException e) {

?// TODO Auto-generated catch block

? e.printStackTrace();

? }

?}

上面的代码,我们在第一篇中已经讲解过了,这里我们会重点看

[java]view plaincopy

?addDownloadBlock(getDownloadBlock(start, len));

其意思是将当前的切分出来的Block放入到待下载队列中去。

而我们在守护进程那里,看到他不断的会从当前系统中找最需要下载的Download,然后再从Download中取出下载队列的Block进行下载

[java]view plaincopy

?//DownloadDemen不断的获取DownloadBlock

?while(true){

? block = getDownloadBlock();

?if(block != null){

? System.out.println("Create new download thread for " + block);

? threadPool.execute(new DownloadThread(block));

? block.getDownload().removeDownloadBlock(block);

? }

?}

[java]view plaincopy

?

//getDownloadBlock()定义如下:

?private DownloadBlock getDownloadBlock(){

? downs= downloads.toArray();

?if(downs== null || downs.length == 0)

?return null;

? Downloaddownload = downs[0];

?for(Downloaddown : downs){

?if(down.getRemainThread()> download.getRemainThread())

? download= down;

? }

? download.descRemainThread();

?return download.getDownloadBlock();

?}

?

[java]view plaincopy

?//而download.getDownloadBlock()定义如下所示:

?public DownloadBlock getDownloadBlock(){

?return blockQueue.peek();

?}

写到这里,整个的系统框架目录就非常清晰了:Downloader, DownloadDemen, Download 之间是通过DownloadBlock联系起来的。

当有一个文件需要下载时,Downloader 把该Download加入到DownloadList中。而Download自身会通过切分文件创建出多个DownloadBlock。DownloadDemen每时每刻都在获取DownloadBlock,赋予其线程进行下载。

下一节,我们会重点讲解一下Downloader如何系统中的缓存进行处理的。

基于Java多线程的下载器源码剖析(三)

分类:Java多线程2012-04-29 11:29 657人阅读评论(12) 收藏举报

多线程javadownload任务concurrency网络

目录(?)[+]四:缓存管理

在Thread切分文件时,会创建非常多的DownloadBlock,为了减少创建、销毁Block 所带来对效率上的影响,我们会把已经创建好的DownloadBlock放入到缓存中,当Download需要DownloadBlock时直接从缓存中取得。即我们使用缓存来对已经申请

的内存重复利用。

在每个Download类中都有一个缓存池,即

ConcurrentLinkedQueue blockCache;

而整个的系统Downloader也会有一个大的

ConcurrentLinkedQueue blockCache;

那么这两者是怎样的关系呢?

在Download类对文件进行切分的时候,需要创建DownloadBlock,而DownloadBlock是根据getDownloadBlock函数来获取到的。

[java]view plaincopy

?/**

?* 从block cache获取一块DownloadBlock。

?* cache中的block都是已经下载完毕的。

?*

?* @param start block块的下载开始位置

?* @param length block块需要下载的长度

?* @return 返回一个创建好的block块。

?*/

?public DownloadBlock getDownloadBlock(long start, long length){

? DownloadBlock block;

?//先从当前的Download中看是否有

?if(blockCache.size() > 0)

? block = blockCache.poll();

?else{

? block = Downloader.getInstance().getDownloadBlock();

? block.setDownload(this);

? }

? block.setStart(start);

? block.setLength(length);

?return block;

?}

上面的代码中,首先会从当前Download所拥有的Cache中获取DownloadBlock,如果有空闲的DownloadBlock,则直接取出。如果没有则需要从当前系统中去申请。

当当前的DownloadBlock使用DownloadThread下载完毕之后,他所拥有的这个DownloadBlock 会交给其所在的Download,以供剩余新建的DownloadBlock使用。

[java]view plaincopy

?block.getDownload().putDownloadBlock(block);

而当整个文件都下载完毕之后,需要把当前Download的所有缓存归还给系统(Downloader)[java]view plaincopy

?for(DownloadBlock block : blockCache){

? Downloader.getInstance().putDownloadBlock(block);

?}

总结:上面的代码演示了一个简单的内管管理模块。而为了频繁的申请内存所带来的效率损耗,我们通过建立blockCache用于缓存当前已经申请好的DownloadBlock, 这样可以对已经申请的内存重复利用。

五. 高效的任务管理分析与方案

在浏览网页内容时,网页内容一般是超出屏幕高度的,需要向下滚屏的。如下图所示,用户浏览该网页,迅速滚屏到最下。于是图片的载入是顺序的,用户停留的网页区域图片最后才载入。

我们希望的体验是,如下图所示,用户在浏览网页,已有图片1和图片2已经或正在下载,然后迅速滚屏到最下的过程中,下载任务的优先级不断调整,最终如图所示数字,用户所看到的网页区域最高优先级下载。

我们先实验一下,在限速环境下下载10个相同文件,下面是分别在拥挤与排队模式下的时间轴:

可以看到,一拥而上的下载过程,每个任务的速度被平摊了,所有任务只能全部最后时段完成,而排队模式部分任务排队,部分先快速完成,排队任务随后也能快速完成。

争抢能力与网络连接数

一般来讲网络连接数越多,整体争抢到的资源越多,但不是绝对的线性正比关系的,而且增多到一定程度后网络质量下降。这些取决于复杂的网络环境和本机其他程序的影响。

所以,通过测试我们会通过在不同网络环境下测试,得到最大连接数的参考值。而且,我们无法回避的是,在有些情况下有可能出现,由于最大连接数的限制,而性能没有不限制的好。这里如何动态调整最大连接数,通过技术手段探知网络状况,从而智能调整,应该是一个很好的研究方向。

前后台任务

前台任务,用户操作而等待内容。这是最高优先级的,而且随着用户新操作的出现,新任务赋予更高优先级。可以理解为插到队首。

后台任务,不紧急的或预备给用户使用的。

由于上面的分析,得到通用的方案是:多个优先级的队列。对优先级编号,每个优先级的队列都可以通过插队首或加队尾的方式添加任务。另外还可以提供调整优先级,移动任务的功能。

六. 单例模式

Downloader 代表整个下载系统,整个系统中只有一个实例对象,因此我们需要保证系统中只有一个实例对象。OK ,我们看如何保证系统中只有一个单例模式:

为了保证系统中只有一个实例,就需要限制构造函数的使用,因为如何用户可以随意调用构造函数,其就可以任意的建立多个实例。因此我们将构造函数定义为private

[java]view plaincopy

private Downloader(DownloaderConfig config) {

?super();

?this.config = config;

? start();

?}

而为了能够得到一个实例,我们在该类中定义了一个静态变量

[java]view plaincopy

?private static Downloader downloader = null;

然后再通过一个public函数得到该Downloader的实例

[java]view plaincopy

?public static Downloader getInstance(){

?if(downloader == null)

? downloader = new Downloader(new DownloaderConfig());

?return downloader;

?}

函数之所以定义为static,是因为我们无法通过建立类对象来调用类的方法,只能通过类名称来调用该方法,因此必须设定为static类型。

这样通过这种方式,我们就可以保证系统中只实例化一个Downloader对象

总结

这个系列总共3篇文章,我们通过这3篇文章讲解了基于Java多线程实现一个下载器的细节,包括如何利用多线程对单个文件进行下载,并实时计算下载速率。以及如何对多个下载进行管理、调度以及内存的管理。

Java第七单元练习题-Java多线程机制

7Java多线程机制 7.1单项选择题 1. 线程调用了sleep()方法后,该线程将进入()状态。 A. 可运行状态 B. 运行状态 C. 阻塞状态 D. 终止状态 2. 关于java线程,下面说法错误的是() A. 线程是以CPU为主体的行为 B. java利用线程使整个系统成为异步 C. 创建线程的方法有两种:实现Runnable接口和继承Thread类 D. 新线程一旦被创建,它将自动开始运行 3. 在java中的线程模型包含() A. 一个虚拟处理器 B. CPU执行的代码 C. 代码操作的数据 D. 以上都是 4.在java语言中,临界区可以是一个语句块,或者是一个方法,并用()关键字标识。 A. synchronized B. include C. import D. Thread 5. 线程控制方法中,yield()的作用是() A. 返回当前线程的引用 B. 使比其低的优先级线程执行 C. 强行终止线程 D. 只让给同优先级线程运行 6. 线程同步中,对象的锁在()情况下持有线程返回 A. 当synchronized()语句块执行完后 B. 当在synchronized()语句块执行中出现例外(exception)时 C. 当持有锁的线程调用该对象的wait()方法时 D. 以上都是 7. 在以下()情况下,线程就进入可运行状态 A. 线程调用了sleep()方法时 B. 线程调用了join()方法时

C. 线程调用了yield()方法时 D. 以上都是 8. java用()机制实现了进程之间的异步执行 A. 监视器 B. 虚拟机 C. 多个CPU D. 异步调用 类的方法中,toString()方法的作用是() A. 只返回线程的名称 B. 返回当前线程所属的线程组的名称 C. 返回当前线程对象 D. 返回线程的名称 语言具有许多优点和特点,下列选项中,哪个反映了Java程序并行机制的特点() A. 安全性 B. 多线程 C. 跨平台 D. 可移值 11.以下哪个关键字可以用来对对象加互斥锁?() A. transient B. synchronized C. serialize D. static 12.下面关于进程、线程的说法不正确的是( )。 A.进程是程序的一次动态执行过程。一个进程在其执行过程中,可以产生多个线程——多线程,形成多条执行线索。 B.线程是比进程更小的执行单位,是在一个进程中独立的控制流,即程序内部的控制流。线程本身不能自动运行,栖身于某个进程之中,由进程启动执行。 C.Java多线程的运行与平台无关。 D.对于单处理器系统,多个线程分时间片获取CPU或其他系统资源来运行。对于多处理器系统,线程可以分配到多个处理器中,从而真正的并发执行多任务。 7.2填空题 1.________是java程序的并发机制,它能同步共享数据、处理不同的事件。 2.线程是程序中的一个执行流,一个执行流是由CPU运行程序的代码、__________所形 成的,因此,线程被认为是以CPU为主体的行为。 3.线程的终止一般可以通过两种方法实现:自然撤销或者是__________. 4.线程模型在java中是由__________类进行定义和描述的。 5.线程的创建有两种方法:实现_________接口和继承Thread类。 6.多线程程序设计的含义是可以将程序任务分成几个________的子任务。 7.按照线程的模型,一个具体的线程也是由虚拟的CPU、代码与数据组成,其中代码与数 据构成了___________,线程的行为由它决定。 8.ava中,新建的线程调用start()方法、如(),将使线程的状态从New(新建状态)转换为 _________。 9.多线程是java程序的________机制,它能同步共享数据,处理不同事件。 10.进程是由代码、数据、内核状态和一组寄存器组成,而线程是表示程序运行状态的

java多线程面试题

java多线程面试题 1.什么是多线程编程?什么时候使用? 多线程一般用于当一个程序需要同时做一个以上的任务。多线程通常用于GUI交互程序。一个新的线程被创建做一些耗时的工作,当主线程保持界面与用户的交互。 2.为什么wait(),notify()和notifyall()函数定义在Object类里面? 因为所有类都是继承于Object类,这样所有类就可以简单的进行多线程编程了。 3.wait()方法和sleep()方法有什么不同? sleep()方法执行后仍然拥有线程,只是延时。而wait方法放弃了线程控制,其它线程可以运行,想要再次运行是要重新开始。 4.Thread和Runnable有什么不同? JA V A线程控制着程序执行的主路径。当你用java命令调用JVM时,JVM创建了一个隐式线程来执行main方法。Thread类提供了主线程调用其它线程并行运行的机制。 Runnable接口定义了一个能被Thread运行的类。实现Runnable的类只需要实行run方法。可以很灵活的扩展现在的已经继承自其它父类的类。而thread则不可以,因为java 只允许继承一个父类。 Runnable可以共享数据,Thread是一个类,而Runnable是一个接口 5.我可以重载start()方法么? 可以重载,重载后还要重载run()方法, 9.编译运行下面的代码会发生什么? 1.public class Bground extends Thread{ 2.public static void main(String argv[]) 3.{ 4. Bground b = new Bground(); 5. b.run(); 6.} 7.public void start()

JAVA 面试题总览(书签完整版)

JAVA面试题总览 JAVA基础 1.JAVA中的几种基本数据类型是什么,各自占用多少字节。 2.String类能被继承吗,为什么。 3.String,Stringbuffer,StringBuilder的区别。 4.ArrayList和LinkedList有什么区别。 5.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数, 字段,当new的时候,他们的执行顺序。 6.用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么, 他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。 7.JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计, 你如何设计。 8.有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。 9.抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接 口么。 10.继承和聚合的区别在哪。 11.IO模型有哪些,讲讲你理解的nio,他和bio,aio的区别是啥,谈谈reactor模型。 12.反射的原理,反射创建类实例的三种方式是什么。 13.反射中,Class.forName和ClassLoader区别。 14.描述动态代理的几种实现方式,分别说出相应的优缺点。 15.动态代理与cglib实现的区别。 16.为什么CGlib方式可以对接口实现代理。 17.final的用途。 18.写出三种单例模式实现。 19.如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣。 20.请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应 用设计中的作用。 21.深拷贝和浅拷贝区别。 22.数组和链表数据结构描述,各自的时间复杂度。 23.error和exception的区别,CheckedException,RuntimeException的区别。 24.请列出5个运行时异常。 25.在自己的代码中,如果创建一个https://www.doczj.com/doc/4f4382644.html,ng.String类,这个类是否可以被类加载器加 载?为什么。

java线程练习题及答案

线程与线程类 1 线程的概念 线程的概念来源于计算机的操作系统的进程的概念。进程是一个程序关于某个数据集的一次运行。也就是说,进程是运行中的程序,是程序的一次运行活动。 线程和进程的相似之处在于,线程和运行的程序都是单个顺序控制流。有些教材将线程称为轻量级进程(light weight process)。线程被看作是轻量级进程是因为它运行在一个程序的上下文内,并利用分配给程序的资源和环境。 作为单个顺序控制流,线程必须在运行的程序中得到自己运行的资源,如必须有自己的执行栈和程序计数器。线程内运行的代码只能在该上下文内。因此还有些教程将执行上下文(execution context)作为线程的同义词。 所有的程序员都熟悉顺序程序的编写,如我们编写的名称排序和求素数的程序就是顺序程序。顺序程序都有开始、执行序列和结束,在程序执行的任何时刻,只有一个执行点。线程(thread )则是进程中的一个单个的顺序控制流。单线程的概念很简单,如图1所示。 多线程(multi-thread )是指在单个的程序内可以同时运行多个不同的线程完成不同的任务,图2说明了一个程序中同时有两个线程运行。 图1 单线程程序示意图 图2 多线程程序示意图 有些程序中需要多个控制流并行执行。例如, for(int i = 0; i < 100; i++) System.out.println("Runner A = " + i); for(int j = 0; j < 100; j++ ) System.out.println("Runner B = "+j); 上面的代码段中,在只支持单线程的语言中,前一个循环不执行完不可能执行第二个循环。要使两个循环同时执行,需要编写多线程的程序。 很多应用程序是用多线程实现的,如Hot Java Web 浏览器就是多线程应用的例子。在Hot Java 浏览器中,你可以一边滚动屏幕,一边下载Applet 或图像,可以同时播放动画和声音等。 2 Thread 类和Runnable 接口 多线程是一个程序中可以有多段代码同时运行,那么这些代码写在哪里,如何创建线程对象呢? 首先,我们来看Java 语言实现多线程编程的类和接口。在https://www.doczj.com/doc/4f4382644.html,ng 包中定义了Runnable 接口和Thread 类。

多线程常见面试题

1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完 后执行? T1.start(); T1.join(); T2.start(); T2.join(); T3.start() 2)11) 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run() 方法? start()方法最本质的功能是从CPU中申请另一个线程空间来执行run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间运行 ,也就是说,如果你直接调用线程对象的run()方法,当然也会执行,但那是在当前线程中执行,run()方法执行完成后继续执行下面的代码.而调用start()方法后,run()方法的代码会和当前线程并发(单CPU)或并行(多CPU)执行。 调用线程对象的run方法不会产生一个新的线程 3)在java中wait和sleep方法的不同? sleep()睡眠时,保持对象锁,仍然占有该锁; 而wait()睡眠时,释放对象锁。 sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会; sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。 在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。 wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问; wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。 wiat()必须放在synchronized block中,否则会在program runtime时扔出”https://www.doczj.com/doc/4f4382644.html,ng.IllegalMonitorStateException“异常。 4)为什么wait, notify 和notifyAll这些方法不在thread类里面? 因为这些是关于锁的 而锁是针对对象的 锁用于线程的同步应用 决定当前对象的锁的方法就应该在对象中吧 我是这么理解的希望对你有帮助

4:一个经典的多线程同步问题汇总

一个经典的多线程同步问题 程序描述: 主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量。 要求: 1.子线程输出的线程序号不能重复。 2.全局变量的输出必须递增。 下面画了个简单的示意图: 分析下这个问题的考察点,主要考察点有二个: 1.主线程创建子线程并传入一个指向变量地址的指针作参数,由于线程启动须要花费一定的时间,所以在子线程根据这个指针访问并保存数据前,主线程应等待子线程保存完毕后才能改动该参数并启动下一个线程。这涉及到主线程与子线程之间的同步。 2.子线程之间会互斥的改动和输出全局变量。要求全局变量的输出必须递增。这涉及到各子线程间的互斥。 下面列出这个程序的基本框架,可以在此代码基础上进行修改和验证。 //经典线程同步互斥问题 #include #include #include long g_nNum; //全局资源 unsigned int__stdcall Fun(void *pPM); //线程函数 const int THREAD_NUM = 10; //子线程个数 int main() { g_nNum = 0;

HANDLE handle[THREAD_NUM]; int i = 0; while (i < THREAD_NUM) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); i++;//等子线程接收到参数时主线程可能改变了这个i的值} //保证子线程已全部运行结束 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); return 0; } unsigned int__stdcall Fun(void *pPM) { //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来int nThreadNum = *(int *)pPM; //子线程获取参数 Sleep(50);//some work should to do g_nNum++; //处理全局资源 Sleep(0);//some work should to do printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum); return 0; } 运行结果:

Java第七单元练习题Java多线程机制

J a v a第七单元练习题 J a v a多线程机制 The latest revision on November 22, 2020

7Java多线程机制 7.1单项选择题 1. 线程调用了sleep()方法后,该线程将进入()状态。 A. 可运行状态 B. 运行状态 C. 阻塞状态 D. 终止状态 2. 关于java线程,下面说法错误的是() A. 线程是以CPU为主体的行为 B. java利用线程使整个系统成为异步 C. 创建线程的方法有两种:实现Runnable接口和继承Thread类 D. 新线程一旦被创建,它将自动开始运行 3. 在java中的线程模型包含() A. 一个虚拟处理器 B. CPU执行的代码 C. 代码操作的数据 D. 以上都是 4.在java语言中,临界区可以是一个语句块,或者是一个方法,并用()关键字标识。 A. synchronized B. include C. import D. Thread 5. 线程控制方法中,yield()的作用是() A. 返回当前线程的引用 B. 使比其低的优先级线程执行 C. 强行终止线程 D. 只让给同优先级线程运行 6. 线程同步中,对象的锁在()情况下持有线程返回 A. 当synchronized()语句块执行完后 B. 当在synchronized()语句块执行中出现例外(exception)时 C. 当持有锁的线程调用该对象的wait()方法时 D. 以上都是 7. 在以下()情况下,线程就进入可运行状态 A. 线程调用了sleep()方法时 B. 线程调用了join()方法时 C. 线程调用了yield()方法时 D. 以上都是 8. java用()机制实现了进程之间的异步执行

用多线程同步方法解决生产者-消费者问题(操作系统课设)

. 题目用多线程同步方法解决生产者-消费 者问题(Producer-Consumer Problem) 学院计算机科学与技术学院 专业软件工程 班级 姓名 指导教师 年月日

目录 目录 (1) 课程设计任务书 (2) 正文 (2) 1.设计目的与要求 (2) 1.1设计目的 (2) 1.2设计要求 (2) 2.设计思想及系统平台 (2) 2.1设计思想 (2) 2.2系统平台及使用语言 (2) 3.详细算法描述 (3) 4.源程序清单 (5) 5.运行结果与运行情况 (10) 6.调试过程 (15) 7.总结 (15) 本科生课程设计成绩评定表 (16)

课程设计任务书 学生姓名:专业班级: 指导教师:工作单位:计算机科学与技术学院 题目: 用多线程同步方法解决生产者-消费者问题 (Producer-Consumer Problem) 初始条件: 1.操作系统:Linux 2.程序设计语言:C语言 3.有界缓冲区内设有20个存储单元,其初值为0。放入/取出的数据项按增序设定为1-20这20个整型数。 要求完成的主要任务:(包括课程设计工作量及其技术要求,以及说明书撰写等具体要 求) 1.技术要求: 1)为每个生产者/消费者产生一个线程,设计正确的同步算法 2)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的当前全部内容、当前指针位置和生产者/消费者线程的自定义标识符。 3)生产者和消费者各有两个以上。 4)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。 2.设计说明书内容要求: 1)设计题目与要求 2)总的设计思想及系统平台、语言、工具等。 3)数据结构与模块说明(功能与流程图) 4)给出用户名、源程序名、目标程序名和源程序及其运行结果。(要注明存储各个程序及其运行结果的主机IP地址和目录。) 5)运行结果与运行情况 (提示: (1)有界缓冲区可用数组实现。 (2)编译命令可用:cc -lpthread -o 目标文件名源文件名 (3)多线程编程方法参见附件。) 3. 调试报告: 1)调试记录 2)自我评析和总结 上机时间安排: 18周一~ 五 08:0 - 12:00 指导教师签名:年月日

精选大厂java多线程面试题50题

Java多线程50题 1)什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。 2)线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。更多详细信息请点击这里。 3)如何在Java中实现线程? https://www.doczj.com/doc/4f4382644.html,ng.Thread类的实例就是一个线程但是它需要调用https://www.doczj.com/doc/4f4382644.html,ng.Runnable接口来执行,由于线程类本身就是调用的 Runnable接口所以你可以继承https://www.doczj.com/doc/4f4382644.html,ng.Thread类或者直接调用Runnable接口来重写run()方法实现线程。 4)Thread类中的start()和run()方法有什么区别? 这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你

调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。 5)Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。 6)Java内存模型是什么? Java内存模型规定和指引Java程序在不同的内存架构、CPU 和操作系统间有确定性地行为。它在多线程的情况下尤其重要。 Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。 ●线程内的代码能够按先后顺序执行,这被称为程序次序 规则。 ●对于同一个锁,一个解锁操作一定要发生在时间上后发 生的另一个锁定操作之前,也叫做管程锁定规则。 ●前一个对Volatile的写操作在后一个volatile的读操作之 前,也叫volatile变量规则。 ●一个线程内的任何操作必需在这个线程的start()调用之 后,也叫作线程启动规则。 ●一个线程的所有操作都会在线程终止之前,线程终止规

四种进程或线程同步互斥的控制方法

四种进程或线程同步互斥的控制方法 1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 2、互斥量:为协调共同对一个共享资源的单独访问而设计的。 3、信号量:为控制一个具有有限数量用户资源而设计。 4、事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 一临界区 临界区的使用在线程同步中应该算是比较简单,说它简单还是说它同后面讲到的其它方法相比更容易理解。举个简单的例子:比如说有一个全局变量(公共资源)两个线程都会对它进行写操作和读操作,如果我们在这里不加以控制,会产生意想不到的结果。假设线程A 正在把全局变量加1然后打印在屏幕上,但是这时切换到线程B,线程B又把全局变量加1然后又切换到线程A,这时候线程A打印的结果就不是程序想要的结果,也就产生了错误。解决的办法就是设置一个区域,让线程A在操纵全局变量的时候进行加锁,线程B如果想操纵这个全局变量就要等待线程A释放这个锁,这个也就是临界区的概念。 二互斥体 windows api中提供了一个互斥体,功能上要比临界区强大。也许你要问,这个东东和临界区有什么区别,为什么强大?它们有以下几点不一致: 1.critical section是局部对象,而mutex是核心对象。因此像waitforsingleobject是不可以等待临界区的。 2.critical section是快速高效的,而mutex同其相比要慢很多 3.critical section使用围是单一进程中的各个线程,而mutex由于可以有一个名字,因此它是可以应用于不同的进程,当然也可以应用于同一个进程中的不同线程。 4.critical section 无法检测到是否被某一个线程释放,而mutex在某一个线程结束之后会产生一个abandoned的信息。同时mutex只能被拥有它的线程释放。下面举两个应用mutex 的例子,一个是程序只能运行一个实例,也就是说同一个程序如果已经运行了,就不能再运行了;另一个是关于非常经典的哲学家吃饭问题的例子。 三事件 事件对象的特点是它可以应用在重叠I/O(overlapped I/0)上,比如说socket编程中有两种模型,一种是重叠I/0,一种是完成端口都是可以使用事件同步。它也是核心对象,因此可以被waitforsingleobje这些函数等待;事件可以有名字,因此可以被其他进程开启。 四信号量 semaphore的概念理解起来可能要比mutex还难,我先简单说一下创建信号量的函数,因为我在开始使用的时候没有很快弄清楚,可能现在还有理解不对的地方,如果有错误还是请大侠多多指教。 CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD LONG lInitialCount, // initial count LONG lMaximumCount, // maximum count LPCTSTR lpName // object name )

java多线程实现调度

重庆交通大学综合性设计性实验报告 实验项目名称:进程调度(先来先服务) 实验项目性质: JAVA多线程 实验所属课程: JAVA程序设计 实验室(中心):语音大楼 8 楼 801 班级:软件专业 2012级2班 姓名:尚亚* 学号: 631206050216 指导教师:杨 实验完成时间: 2014 年 11 月 25 日

一、实验目的 1、理解程序、线程和进程的概念; 2、理解多线程的概念; 3、掌握线程的各种状态; 4、熟练使用Thread类创建线程; 5、熟练使用线程各种方法; 6、掌握线程的调度及线程同步的实现原理。 二、实验内容及要求 进程调度是处理机管理的核心内容。本实验要求采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法编写和调试一个简单的进程调度程序。通过本实验可以加深理解有关进程控制块、进程队列的概念。并体会了优先数和先来先服务调度算法的具体实施办法。 用JA V A语言编写和调试一个进程调度程序,以加深对进程的概念及进程调度算法的理解。做一个能够直观体现多个进程时,CPU 是怎样调度就绪队列中的进程(按照先来先服务的原则)。

三、实验设备 PC机,windows7,eclipse。 四、设计方案 ㈠设计主要思想 (1)要模拟进程的调度算法,必须先体现处进程及系统资源。 (2)要体现先来先服务的算法,就必须表现出当有一个进程进入CPU时其他进程不能进入,并在就绪队列中排队。本实验建立了四个圆移动的线程表示作业调度,用圆在表示就绪队列的方框中停留表示进程在就绪队列中排队。 (3)当有一个圆移动到表示CPU的范围内时,让其它线程在就绪队列中排队,当CPU内无进程时,先来的圆先移动,以表示CPU 对进程的调度。 ㈡设计的主要步骤 (1)建立四个不同颜色的圆移动的线程,表示对四个进程的调度。 (2)当有一个表示进程的圆到达表示CPU范围内时,通过让其它几个圆停留在表示就绪队列的方框范围内,表示进程在就绪队列中排成队列。 (3)当第一个先到达的进程释放CPU,在排成队列的几个圆中选择先到达的圆,使其移动表示对先来的进程进行调度,直到所有的圆移动完毕。 五、主要代码 import java.awt.Font; import java.awt.event.*;

2019最新Java面试题,常见面试题及答案汇总

ava最新常见面试题+ 答案汇总 1、面试题模块汇总 面试题包括以下十九个模块:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql、Redis、JVM 。如下图所示: 可能对于初学者不需要后面的框架和JVM 模块的知识,读者朋友们可根据自己的情况,选择对应的模块进行阅读。 适宜阅读人群 需要面试的初/中/高级java 程序员 想要查漏补缺的人 想要不断完善和扩充自己java 技术栈的人 java 面试官 具体面试题 下面一起来看208 道面试题,具体的内容。 一、Java 基础 1.JDK 和JRE 有什么区别? 2.== 和equals 的区别是什么? 3.两个对象的hashCode()相同,则equals()也一定为true,对吗? 4.final 在java 中有什么作用? 5.java 中的Math.round(-1.5) 等于多少? 6.String 属于基础的数据类型吗? 7.java 中操作字符串都有哪些类?它们之间有什么区别? 8.String str="i"与String str=new String(“i”)一样吗? 9.如何将字符串反转? 10.String 类的常用方法都有那些? 11.抽象类必须要有抽象方法吗? 12.普通类和抽象类有哪些区别? 13.抽象类能使用final 修饰吗?

14.接口和抽象类有什么区别? 15.java 中IO 流分为几种? 16.BIO、NIO、AIO 有什么区别? 17.Files的常用方法都有哪些? 二、容器 18.java 容器都有哪些? 19.Collection 和Collections 有什么区别? 20.List、Set、Map 之间的区别是什么? 21.HashMap 和Hashtable 有什么区别? 22.如何决定使用HashMap 还是TreeMap? 23.说一下HashMap 的实现原理? 24.说一下HashSet 的实现原理? 25.ArrayList 和LinkedList 的区别是什么? 26.如何实现数组和List 之间的转换? 27.ArrayList 和Vector 的区别是什么? 28.Array 和ArrayList 有何区别? 29.在Queue 中poll()和remove()有什么区别? 30.哪些集合类是线程安全的? 31.迭代器Iterator 是什么? 32.Iterator 怎么使用?有什么特点? 33.Iterator 和ListIterator 有什么区别? 34.怎么确保一个集合不能被修改?

15个Java多线程面试题及答案

15个Java多线程面试题及答案 1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。 2)在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编程中最大的优势是它们为读和写分别提 供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。芯学苑老师强烈建议在你在面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 3)在java中wait和sleep方法的不同?

通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。 4)用Java实现阻塞队列。 这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用Java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。 5)用Java写代码来解决生产者——消费者问题。 与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在Java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。 6)用Java编程一个会导致死锁的程序,你将怎么解决?

用多线程同步方法解决生产者-消费者问题(操作系统课设)

用多线程同步方法解决生产者-消费者问题(操作系统课设)

题目 用多线程同步方法解决生产者-消费 者问题(Producer-Consume r Problem) 学院 物理学与电子信息工程学院 专业电子信息工程班级08电信本一班姓名 指导教师 2010 年12 月日

目录 目录 0 课程设计任务书 (1) 正文 (3) 1.设计目的与要求 (3) 1.1设计目的 (3) 1.2设计要求 (3) 2.设计思想及系统平台 (3) 2.1设计思想 (3) 2.2系统平台及使用语言 (3) 3.详细算法描述 (4) 4.源程序清单 (7) 5.运行结果与运行情况 (12) 6.调试过程 (16) 7.总结 (16)

课程设计任务书 题目: 用多线程同步方法解决生产者-消费者问题 (Producer-Consumer Problem) 初始条件: 1.操作系统:Linux 2.程序设计语言:C语言 3.有界缓冲区内设有20个存储单元,其初 值为0。放入/取出的数据项按增序设定为 1-20这20个整型数。 要求完成的主要任务:(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求) 1.技术要求: 1)为每个生产者/消费者产生一个线程,设计正确的同步算法 2)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的当前全部 内容、当前指针位置和生产者/消费者

线程的自定义标识符。 3)生产者和消费者各有两个以上。 4)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。 2.设计说明书内容要求: 1)设计题目与要求 2)总的设计思想及系统平台、语言、工具 等。 3)数据结构与模块说明(功能与流程图) 4)给出用户名、源程序名、目标程序名和源程序及其运行结果。(要注明存储各个 程序及其运行结果的主机IP地址和目 录。) 5)运行结果与运行情况 (提示: (1)有界缓冲区可用数组实现。 (2)编译命令可用:cc -lpthread -o 目标文件名源文件名 (3)多线程编程方法参见附件。) 3. 调试报告: 1)调试记录 2)自我评析和总结

Java第七单元练习题Java多线程机制(20210109223217)

7 Java 多线程机制 7.1 单项选择题 1. 线程调用了sleep ()方法后,该线程将进入( )状态。 A. 可运行状态 B. 运行状态 C. 阻塞状态 D. 终止状态 2. 关于java 线程,下面说法错误的是() A. 线程是以CPU为主体的行为 B. java 利用线程使整个系统成为异步 C. 创建线程的方法有两种:实现Runnable 接口和继承Thread 类 D. 新线程一旦被创建,它将自动开始运行 3. 在java 中的线程模型包含() A. 一个虚拟处理器 B. CPU执行的代码 C. 代码操作的数据 D. 以上都是 4. 在java 语言中,临界区可以是一个语句块,或者是一个方法,并用()关键字标识。 A. synchronized B. include C. import D. Thread 5. 线程控制方法中,yield()的作用是() A. 返回当前线程的引用 B. 使比其低的优先级线程执行 C. 强行终止线程 D. 只让给同优先级线程运行 6. 线程同步中,对象的锁在()情况下持有线程返回 A. 当synchronized()语句块执行完后 B. 当在synchronized()语句块执行中出现例外( exception )时 C. 当持有锁的线程调用该对象的wait()方法时 D. 以上都是 7. 在以下()情况下,线程就进入可运行状态 A. 线程调用了sleep()方法时 B. 线程调用了join()方法时 C. 线程调用了yield()方法时 D. 以上都是 8. java 用()机制实现了进程之间的异步执行 A. 监视器 B. 虚拟机

多线程与并发面试题

多线程与并发面试题

JAVA多线程和并发基础面试问答 原文链接译文连接作者:Pankaj 译者:郑旭东校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,可是你依然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它能够被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程能够被称为轻量级进程。线程需要较少的资源来创立和驻留在进程中,而且能够共享进程中的资源。 2. 多线程编程的好处是什么?

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创立多个线程去执行一些任务会比创立多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。 3. 用户线程和守护线程有什么区别? 当我们在Java程序中创立一个线程,它就被称为用户线程。一个守护线程是在后台执行而且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序而且退出。一个守护线程创立的子线程依然是守护线程。 4. 我们如何创立一个线程? 有两种创立线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创立一个Thread对象;二是直接继承Thread类。若想了解更多能够阅读这篇关于如何在Java中创立线程的文章。 5. 有哪些不同的线程生命周期?

多线程同步方法及比较

多线程同步方法及比较 多线程同步方法: 1.临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数 据访问。. 2.互斥量:为协调一起对一个共享资源的单独访问而设计的。. 3.信号量:为控制一个具备有限数量用户资源而设计。. 4.事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 临界区(Critical Section).. 确保在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。假如有多个线程试图同时访问临界区,那么在有一个线程进入后其他任何试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程能够继续抢占,并以此达到用原子方式操作共享资源的目的。 临界区包含两个操作原语: EnterCriticalSection()进入临界区 LeaveCriticalSection()离开临界区。 EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保和之匹配的LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。 MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是很简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。. ------------------------------------------------

线程编程方面笔试题

线程编程方面java笔试题 60、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 答:有两种实现方法,分别是继承Thread类与实现Runnable接口 用synchronized关键字修饰同步方法 反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。 61、sleep() 和 wait() 有什么区别? 答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 62、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。 63、启动一个线程是用run()还是start()? 答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 64、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 答:不能,一个对象的一个synchronized方法只能由一个线程访问。 65、请说出你所知道的线程同步的方法。 答:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。 sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉Interrupt edException异常。 notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。 66、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify

相关主题
文本预览
相关文档 最新文档