Java java多线程安全问题问题

Java 多线程下载进度问题
代码贴在一楼,如果没有加同步块虽然下载完毕的文件是正确的但是进度确实99%,加了同步块之后就是100%了。想了好几天没想出来到底是哪里出了同步问题。加法不应该会有同步问题啊。麻烦老师帮忙看下。
package com.java.
import java.io.F
import java.io.FileNotFoundE
import java.io.IOE
import java.io.InputS
import java.io.RandomAccessF
import java.net.HttpURLC
import java.net.MalformedURLE
import java.net.URL;
public class UpdateReceiver {
& && &&&private static int length=0;
& && &&&private RandomAccessFile accessF
& && &&&private static Object object=new Object();
& && &&&public static void main(String[] args) {
& && && && && & new UpdateReceiver().download(&/data/wisegame/3cf61f0c7216ed52/UCliulanqi_90.apk&,3); // 开启三条线程进行文件的下载
& && &&&}
& && &&&/**
& && && &* 多线程下载文件
& && && &*
& && && &* @param path
& && && &*& && && && &网络文件URL
& && && &* @param threadNum
& && && &*& && && && &开启的线程数
& && && &*/
& && &&&public void download(String path, int threadNum) {
& && && && && & try {
& && && && && && && && &URL url = new URL(path);
& && && && && && && && &HttpURLConnection conn = (HttpURLConnection) url.openConnection();
& && && && && && && && &conn.setConnectTimeout(3000);
& && && && && && && && &conn.setRequestMethod(&GET&);
& && && && && && && && &if (conn.getResponseCode() == 200) {
& && && && && && && && && && &&&int fileLength = conn.getContentLength(); // 获取网络文件大小
& && && && && && && && && && &&&File file = new File(&d://a&, &b.exe&);
& && && && && && && && && && &&&accessFile = new RandomAccessFile(file, &rwd&);
& && && && && && && && && && &&&accessFile.setLength(fileLength); // 在本地生成相同大小的一个文件 }
& && && && && && && && && && &&&int block = fileLength % threadNum == 0 ? fileLength/ threadNum : (fileLength / threadNum) + 1; // 每条线程负责下载的数据量
& && && && && && && && && && &&&for (int threadId = 0; threadId & threadN threadId++) {
& && && && && && && && && && && && && & new DownloadThread(threadId, block, path, file).start();
& && && && && && && && && && &&&}
& && && && && && && && &}
& && && && && & } catch (MalformedURLException e) {
& && && && && && && && &e.printStackTrace();
& && && && && & } catch (IOException e) {
& && && && && && && && &e.printStackTrace();
& && && && && & }
& && &&&}
& && &&&public class DownloadThread extends Thread {
& && && && && & private int threadId;
& && && && && &
& && && && && & private S
& && && && && & private F
& && && && && & public DownloadThread(int threadId, int block, String path, File file) {
& && && && && && && && &this.threadId = threadId;
& && && && && && && && &this.block =
& && && && && && && && &this.path =
& && && && && && && && &this.file =
& && && && && & }
& && && && && & public void run() {
& && && && && && && && &super.run();
& && && && && && && && &int start = threadId * // 计算线程从网络文件哪一处开始下载
& && && && && && && && &int end = (threadId + 1) * block - 1; // 计算线程下载到网络文件的什么位置
& && && && && && && && &InputStream in =
& && && && && && && && &RandomAccessFile accessFile =
& && && && && && && && &try {
& && && && && && && && && && &&&accessFile = new RandomAccessFile(file, &rwd&);
& && && && && && && && && && &&&accessFile.seek(start);
& && && && && && && && && && &&&URL url = new URL(path);
& && && && && && && && && && &&&HttpURLConnection conn = (HttpURLConnection) url.openConnection();
& && && && && && && && && && &&&conn.setConnectTimeout(3000);
& && && && && && && && && && &&&conn.setRequestMethod(&GET&);
& && && && && && && && && && &&&conn.setRequestProperty(&Range&, &bytes=& + start + &-& + end);// 设置该头字段表示下载一部分数据
& && && && && && && && && && &&&if (conn.getResponseCode() == 206) {// 请求范围时返回码应为206
& && && && && && && && && && && && && & in = conn.getInputStream();
& && && && && && && && && && && && && & byte[] buffer = new byte[1024];
& && && && && && && && && && && && && & int len = 0;
& && && && && && && && && && && && && & while ((len = in.read(buffer)) != -1) {
& && && && && && && && && && && && && && && && &synchronized (object) {
& && && && && && && && && && && && && && && && && && &&&accessFile.write(buffer,0, len);
& && && && && && && && && && && && && && && && && && &&&length+=
& && && && && && && && && && && && && && && && && && &&&System.out.println(length*100/accessFile.length()+&%&);
& && && && && && && && && && && && && && && && &}
& && && && && && && && && && && && && & }
& && && && && && && && && && &&&}
& && && && && && && && &} catch (FileNotFoundException e) {
& && && && && && && && && && &&&e.printStackTrace();
& && && && && && && && &} catch (IOException e) {
& && && && && && && && && && &&&e.printStackTrace();
& && && && && && && && &} finally {
& && && && && && && && && && &&&try {
& && && && && && && && && && && && && & System.out.println(&length:&+length);
& && && && && && && && && && && && && & System.out.println(&第&+(threadId+1)+&条线程下载完毕&);
& && && && && && && && && && && && && & if (accessFile != null && in != null) {
& && && && && && && && && && && && && && && && &accessFile.close();
& && && && && && && && && && && && && && && && &in.close();
& && && && && && && && && && && && && & }
& && && && && && && && && && &&&} catch (IOException e) {
& && && && && && && && && && && && && & e.printStackTrace();
& && && && && && && && && && &&&}
& && && && && && && && &}
& && && && && & }
就是 synchronized (object) {
& && && && && && && && && && && && && && && && && && &&&accessFile.write(buffer,0, len);
& && && && && && && && && && && && && && && && && && &&&length+=
& && && && && && && && && && && && && && && && && && &&&System.out.println(length*100/accessFile.length()+&%&);
& && && && && && && && && && && && && && && && &}
这段,如果去掉同步块就99%了 加上就100%。求大神指点原因 同步问题出在哪里
可以直接把代码粘到Eclipse中运行一下,可以直接运行的。去掉同步块和加上同步块分别试一下,就知道我的疑惑所在了
大神啊 求解答啊...
找了半天原因,突然发现+=并不是运算符,把他当成一元运算符了。那就是+=这句有问题了,1线程还没有来得及把length+len赋值给length就被其它线程抢去了,这样就会少加了一次。
牛帖: 3 好贴: 3 &
找了半天原因,突然发现+=并不是运算符,把他当成一元运算符了。那就是+=这句有问题了,1线程还没有来得及 ...
这句的意思等同于 length=length+len;
这句的意思等同于 length=length+len;
是这样理解的&&length+len=
& && && && && && &&&length=
实际是两句,也就是这里出现了同步问题
武汉最强,没有之一网络营销课程重磅升级PHP学院落户大上海,一年多赚10万元游戏开发培训传智播客iOS9精品课程
? ? ? ? ? ? ? ? ? ?
Powered bypackage&com.class&MyThread&implements&java.lang.Runnable{&&&&private&int&threadId;&&&&public&MyThread(int&id)&&&&{&&&&&&&&this.threadId&=&&&&&}&&&&@Override&&&&public&synchronized&void&run()&&&&&{&&&&&&&&for&(int&i&=&0;&i&&&100;&++i)&&&&&&&&{&&&&&&&&&&&&System.out.println("Thread&ID:&"&+&this.threadId&+&"&:&"&+&i);&&&&&&&&}&&&&}}public&class&ThreadDemo{&&&&/**&&&&&*&@param&args&&&&&*&@throws&InterruptedException&&&&&&*/&&&&public&static&void&main(String[]&args)&throws&InterruptedException&&&&{&&&&&&&&for&(int&i&=&0;&i&&&10;&++i)&&&&&&&&{&&&&&&&&&&&&new&Thread(new&MyThread(i)).start();&&&&&&&&&&&&Thread.sleep(1);&&&&&&&&}&&&&}}
&&&&& 从上述代码段可以得知,要想实现线程的同步,则这些线程必须去竞争一个唯一的共享的对象锁。
&&&&& 基于这种思想,我们将第一段代码修改如下所示,在创建启动线程之前,先创建一个线程之间竞争使用的Object对象,然后将这个Object对象的引用传递给每一个线程对象的lock成员变量。这样一来,每个线程的lock成员都指向同一个Object对象。我们在run方法中,对lock对象使用synchronzied块进行局部封锁,这样就可以让线程去竞争这个唯一的共享的对象锁,从而实现同步。
代码package&com.class&MyThread&implements&java.lang.Runnable{&&&&private&int&threadId;&&&&private&Object&&&&&public&MyThread(int&id,&Object&obj)&&&&{&&&&&&&&this.threadId&=&&&&&&&&&this.lock&=&&&&&}&&&&@Override&&&&public&&void&run()&&&&&{&&&&&&&&synchronized(lock)&&&&&&&&{&&&&&&&&&&&&for&(int&i&=&0;&i&&&100;&++i)&&&&&&&&&&&&{&&&&&&&&&&&&&&&&System.out.println("Thread&ID:&"&+&this.threadId&+&"&:&"&+&i);&&&&&&&&&&&&}&&&&&&&&}&&&&}}public&class&ThreadDemo{&&&&/**&&&&&*&@param&args&&&&&*&@throws&InterruptedException&&&&&&*/&&&&public&static&void&main(String[]&args)&throws&InterruptedException&&&&{&&&&&&&&Object&obj&=&new&Object();&&&&&&&&for&(int&i&=&0;&i&&&10;&++i)&&&&&&&&{&&&&&&&&&&&&new&Thread(new&MyThread(i,&obj)).start();&&&&&&&&&&&&Thread.sleep(1);&&&&&&&&}&&&&}}
从第二段代码可知,同步的关键是多个线程对象竞争同一个共享资源即可,上面的代码中是通过外部创建共享资源,然后传递到线程中来实现。我们也可以利用类成员变量被所有类的实例所共享这一特性,因此可以将lock用静态成员对象来实现,代码如下所示:&代码package&com.class&MyThread&implements&java.lang.Runnable{&&&&private&int&threadId;&&&&private&static&Object&lock&=&new&Object();&&&&public&MyThread(int&id)&&&&{&&&&&&&&this.threadId&=&&&&&}&&&&@Override&&&&public&&void&run()&&&&&{&&&&&&&&synchronized(lock)&&&&&&&&{&&&&&&&&&&&&for&(int&i&=&0;&i&&&100;&++i)&&&&&&&&&&&&{&&&&&&&&&&&&&&&&System.out.println("Thread&ID:&"&+&this.threadId&+&"&:&"&+&i);&&&&&&&&&&&&}&&&&&&&&}&&&&}}public&class&ThreadDemo&{&&&&/**&&&&&*&@param&args&&&&&*&@throws&InterruptedException&&&&&&*/&&&&public&static&void&main(String[]&args)&throws&InterruptedException&&&&{&&&&&&&&for&(int&i&=&0;&i&&&10;&++i)&&&&&&&&{&&&&&&&&&&&&new&Thread(new&MyThread(i)).start();&&&&&&&&&&&&Thread.sleep(1);&&&&&&&&}&&&&}}
再来看第一段代码,实例方法中加入sychronized关键字封锁的是this对象本身,而在静态方法中加入sychronized关键字封锁的就是类本身。静态方法是所有类实例对象所共享的,因此线程对象在访问此静态方法时是互斥访问的,从而可以实现线程的同步,代码如下所示:代码package&com.class&MyThread&implements&java.lang.Runnable{&&&&private&int&threadId;&&&&&&&&public&MyThread(int&id)&&&&{&&&&&&&&this.threadId&=&&&&&}&&&&@Override&&&&public&&void&run()&&&&&{&&&&&&&&taskHandler(this.threadId);&&&&}&&&&private&static&synchronized&void&taskHandler(int&threadId)&&&&{&&&&&&&&for&(int&i&=&0;&i&&&100;&++i)&&&&&&&&{&&&&&&&&&&&&System.out.println("Thread&ID:&"&+&threadId&+&"&:&"&+&i);&&&&&&&&}&&&&}}public&class&ThreadDemo{&&&&/**&&&&&*&@param&args&&&&&*&@throws&InterruptedException&&&&&&*/&&&&public&static&void&main(String[]&args)&throws&InterruptedException&&&&{&&&&&&&&for&(int&i&=&0;&i&&&10;&++i)&&&&&&&&{&&&&&&&&&&&&new&Thread(new&MyThread(i)).start();&&&&&&&&&&&&Thread.sleep(1);&&&&&&&&}&&&&}}&
随笔 - 607
评论 - 1579
引用 - 117JAVA并发多线程的面试问题及答案(基础部分) _ java面试题_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
JAVA并发多线程的面试问题及答案(基础部分) _ java面试题
阅读已结束,如果下载本文需要使用
想免费下载本文?
你可能喜欢JAVA线程间通信问题 - java技术之家 - ITeye技术网站
博客分类:
在前一小节,介绍了在多线程编程中使用同步机制的重要性,并学会了如何实现同步的方法来正确地访问共享资源。这些线程之间的关系是平等的,彼此之间并不存在任何依赖,它们各自竞争CPU资源,互不相让,并且还无条件地阻止其他线程对共享资源的异步访问。然而,也有很多现实问题要求不仅要同步的访问同一共享资源,而且线程间还彼此牵制,通过相互通信来向前推进。那么,多个线程之间是如何进行通信的呢?解决思路
在现实应用中,很多时候都需要让多个线程按照一定的次序来访问共享资源,例如,经典的生产者和消费者问题。这类问题描述了这样一种情况,假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费。如果仓库中没有产品,则生产者可以将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。显然,这是一个同步问题,生产者和消费者共享同一资源,并且,生产者和消费者之间彼此依赖,互为条件向前推进。但是,该如何编写程序来解决这个问题呢?
传统的思路是利用循环检测的方式来实现,这种方式通过重复检查某一个特定条件是否成立来决定线程的推进顺序。比如,一旦生产者生产结束,它就继续利用循环检测来判断仓库中的产品是否被消费者消费,而消费者也是在消费结束后就会立即使用循环检测的方式来判断仓库中是否又放进产品。显然,这些操作是很耗费CPU资源的,不值得提倡。那么有没有更好的方法来解决这类问题呢?
首先,当线程在继续执行前需要等待一个条件方可继续执行时,仅有 synchronized 关键字是不够的。因为虽然synchronized关键字可以阻止并发更新同一个共享资源,实现了同步,但是它不能用来实现线程间的消息传递,也就是所谓的通信。而在处理此类问题的时候又必须遵循一种原则,即:对于生产者,在生产者没有生产之前,要通知消费者等待;在生产者生产之后,马上又通知消费者消费;对于消费者,在消费者消费之后,要通知生产者已经消费结束,需要继续生产新的产品以供消费。
其实,Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()、notify()和notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。
虽然所有的类都默认拥有这3个方法,但是只有在synchronized关键字作用的范围内,并且是同一个同步问题中搭配使用这3个方法时才有实际的意义。
这些方法在Object类中声明的语法格式如下所示:
final void wait() throws InterruptedException
final void notify()
final void notifyAll()
其中,调用wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行态退出,进入等待队列,直到被再次唤醒。而调用notify()方法可以唤醒等待队列中第一个等待同一共享资源的线程,并使该线程退出等待队列,进入可运行态。调用notifyAll()方法可以使所有正在等待队列中等待同一共享资源的线程从等待状态退出,进入可运行状态,此时,优先级最高的那个线程最先执行。显然,利用这些方法就不必再循环检测共享资源的状态,而是在需要的时候直接唤醒等待队列中的线程就可以了。这样不但节省了宝贵的CPU资源,也提高了程序的效率。
由于wait()方法在声明的时候被声明为抛出InterruptedException异常,因此,在调用wait()方法时,需要将它放入try…catch代码块中。此外,使用该方法时还需要把它放到一个同步代码段中,否则会出现如下异常:
"java.lang.IllegalMonitorStateException: current thread not owner"
这些方法是不是就可以实现线程间的通信了呢?下面将通过多线程同步的模型: 生产者和消费者问题来说明怎样通过程序解决多线程间的通信问题。
下面这个程序演示了多个线程之间进行通信的具体实现过程。程序中用到了4个类,其中ShareData类用来定义共享数据和同步方法。在同步方法中调用了wait()方法和notify()方法,并通过一个信号量来实现线程间的消息传递。
// 例4.6.1
CommunicationDemo.java 描述:生产者和消费者之间的消息传递过程
class ShareData
private boolean isProduced = // 信号量
public synchronized void putShareChar(char c)
// 同步方法putShareChar()
if (isProduced)
// 如果产品还未消费,则生产者等待
// 生产者等待
}catch(InterruptedException e){
e.printStackTrace();
isProduced =
// 标记已经生产
// 通知消费者已经生产,可以消费
public synchronized char getShareChar()
// 同步方法getShareChar()
if (!isProduced)
// 如果产品还未生产,则消费者等待
// 消费者等待
}catch(InterruptedException e){
e.printStackTrace();
isProduced = // 标记已经消费
// 通知需要生产
return this.c;
class Producer extends Thread
// 生产者线程
private ShareD
Producer(ShareData s)
public void run()
for (char ch = 'A'; ch &= 'D'; ch++)
Thread.sleep((int)(Math.random()*3000));
}catch(InterruptedException e){
e.printStackTrace();
s.putShareChar(ch);
// 将产品放入仓库
System.out.println(ch + " is produced by Producer.");
class Consumer extends Thread
// 消费者线程
private ShareD
Consumer(ShareData s)
public void run()
Thread.sleep((int)(Math.random()*3000));
}catch(InterruptedException e){
e.printStackTrace();
ch = s.getShareChar();
// 从仓库中取出产品
System.out.println(ch + " is consumed by Consumer. ");
}while (ch != 'D');
class CommunicationDemo
public static void main(String[] args)
ShareData s = new ShareData();
new Consumer(s).start();
new Producer(s).start();
上面的程序演示了生产者生产出A、B、C、D四个字符,消费者消费这四个字符的全过程,程序结果如图4.6.1所示:
生产者和消费者举例
通过程序的运行结果可以看到,尽管在主方法中先启动了Consumer线程,但是,由于仓库中没有产品,因此,Consumer线程就会调用wait()方法进入等待队列进行等待,直到Producer线程将产品生产出来并放进仓库,然后使用notify()方法将其唤醒。
由于在两个线程中都指定了一定的休眠时间,因此也可能出现这样的情况:生产者将产品生产出来放入仓库,并通知等待队列中的Consumer线程,然而,由于休眠时间过长,Consumer线程还没有打算消费产品,此时,Producer线程欲生产下一个产品,结果由于仓库中的产品没有被消费掉,故Producer线程执行wait()方法进入等待队列等待,直到Consumer线程将仓库中的产品消费掉以后通过notify()方法去唤醒等待队列中的Producer线程为止。可见,两个线程之间除了必须保持同步之外,还要通过相互通信才能继续向前推进。
前面这个程序中,生产者一次只能生产一个产品,而消费者也只能一次消费一个产品。那么现实中也有这样的情况,生产者可以一次生产多个产品,只要仓库容量够大,就可以一直生产。而消费者也可以一次消费多个产品,直到仓库中没有产品为止。
但是,无论是生产产品到仓库,还是从仓库中消费,每一次都只能允许一个操作。显然,这也是个同步问题,只不过在这个问题中共享资源是一个资源池,可以存放多个资源。下面就以栈结构为例给出如何在这个问题中解决线程通信的程序代码。
// 例4.6.2
CommunicationDemo2.java
class SyncStack
// 同步堆栈类,可以一次放入多个数据
private int index = 0; // 堆栈指针初始值为0
private char[] buffer = new char[5]; // 堆栈有5个字符的空间
public synchronized void push(char c) // 入栈同步方法
if(index == buffer.length)
堆栈已满,不能入栈
this.wait();
//等待出栈线程将数据出栈
}catch(InterruptedException e){ }
buffer[index] = // 数据入栈
// 指针加1,栈内空间减少
this.notify();
// 通知其他线程把数据出栈
public synchronized char pop()
// 出栈同步方法
if(index == 0)
堆栈无数据,不能出栈
this.wait();
//等待入栈线程把数据入栈
}catch(InterruptedException e){ }
this.notify(); //通知其他线程入栈
index--; //指针向下移动
return buffer[index]; //数据出栈
class Producer implements Runnable
//生产者类
//生产者类生成的字母都保存到同步堆栈中
public Producer(SyncStack s)
public void run()
for(int i=0; i&5; i++)
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){ }
ch =(char)(Math.random()*26+'A'); //随机产生5个字符
s.push(ch); //把字符入栈
System.out.println("Push "+ch+" in Stack"); // 打印字符入栈
class Consumer implements Runnable
//消费者类
//消费者类获得的字符都来自同步堆栈
public Consumer(SyncStack s)
public void run()
for(int i=0;i&5;i++)
Thread.sleep((int)(Math.random()*3000));
}catch(InterruptedException e){ }
ch = s.pop(); //从堆栈中读取字符
System.out.println("Pop
"+ch+" from Stack"); //打印字符出栈
public class CommunicationDemo2
public static void main(String[] args)
SyncStack stack = new SyncStack();
//下面的消费者类对象和生产者类对象所操作的是同一个同步堆栈对象
Thread t1 = new Thread(new Producer(stack)); //线程实例化
Thread t2 = new Thread(new Consumer(stack)); //线程实例化
t2.start(); //线程启动
t1.start(); //线程启动
程序中引入了一个堆栈数组buffer[]来模拟资源池,并使生产者类和消费者类都实现了Runnable接口,然后在主程序中通过前面介绍的方法创建两个共享同一堆栈资源的线程,并且有意先启动消费者线程,后启动生产者线程。请在阅读程序的时候仔细观察例4.6.1和本例的相似点以及区别之处,体会作者的用心。程序结果输出如图4.6.2所示:
共享资源池的生产者和消费者问题
由于是栈结构,所以符合后进先出原则。有兴趣的读者还可以用符合先进先出原则的队列结构来模拟线程间通信的过程,相信可以通过查阅相关的资料来解决这个问题,在这里就不再给出程序代码了,作为一个思考题供读者练习。
本小节介绍了三个重要的方法:wait()、notify()和notifyAll()。使用它们可以高效率地完成多个线程间的通信问题,这样在通信问题上就不必再使用循环检测的方法来等待某个条件的发生,因为这种方法是极为浪费CPU资源的,当然这种情况也不是所期望的。在例4.6.1中,为了更好地通信,引入了一个专门用来传递信息的信号量。利用信号量来决定线程是否等待无疑是一种非常安全的操作,值得提倡。此外,在例4.6.2中引入了资源池作为共享资源,并解决了在这种情况下如何实现多线程之间的通信问题。希望读者能够举一反三,编写出解决更加复杂问题的程序。
可以肯定的是,合理地使用wait()、notify()和notifyAll()方法确实能够很好地解决线程间通信的问题。但是,也应该了解到这些方法是更复杂的锁定、排队和并发性代码的构件。尤其是使用 notify()来代替notifyAll()时是有风险的。除非确实知道每一个线程正在做什么,否则最好使用notifyAll()。其实,在JDK1.5中已经引入了一个新的包:java.util.concurrent 包,该包是一个被广泛使用的开放源码工具箱,里面都是有用的并发性实用程序。完全可以代替wait()和notify()方法用来编写自己的调度程序和锁。有关信息可以查阅相关资料,本书中不再赘述。
Java提供了各种各样的输入输出流(stream),使程序员能够很方便地对数据进行操作。其中,管道(pipe)流是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输入管道中读出数据。通过使用管道,达到实现多个线程间通信的目的。那么,如何创建和使用管道呢?
Java提供了两个特殊的专门用来处理管道的类,它们就是PipedInputStream类和PipedOutputStream类。
其中,PipedInputStream代表了数据在管道中的输出端,也就是线程从管道读出数据的一端;PipedOutputStream代表了数据在管道中的输入端,也就是线程向管道写入数据的一端,这两个类一起使用就可以创建出数据输入输出的管道流对象。
一旦创建了管道之后,就可以利用多线程的通信机制对磁盘中的文件通过管道进行数据的读写,从而使多线程的程序设计在实际应用中发挥更大的作用。
浏览: 40247 次
来自: 上海
讲得很清晰详细,基本理解了~
这篇文章好,对于信号量进行了解释,并且扩展了线程的实现思考方式 ...
其实上述的问题总归起来还是架构设计不明确,Service层是否 ...
好东西呀!谢谢了

我要回帖

更多关于 java 多线程 的文章

 

随机推荐