工作中遇到的问题,记录下解决的思路

问题:

   对磁盘进行碎片化测试(比如说,磁盘空间是16G),从64K开始写文件,写满后删除一半,然后写32K 的数据,写满后删除一半。。。直到4K写满删除一般算是结束

第一阶段:

  使用单独的一个线程进行操作,先写数据,然后删除数据,用循环控制跳出

代码

public class Task extends Thread{
public void run() {
int size = 64;
while(size >=4){
write(size);
delete();
size /= 2;
}
} private void write(int size){
//省略具体的写数据方法,判断是否写满
} private void delete(){
//省略删除的方法
}
}

上述的代码已经实现了功能,但是如果空间很大,怎么半?一个线程写的也太慢了,如何提高效率,写入的速度?

办法就是使用多线程,多个线程同时写,肯定能提升不小的效率

第二阶段:

  使用多个线程进行操作,代码和第一阶段的代码没有变化,共同写同一个磁盘就行

然后出现了新的问题,多线程操作,到最后肯定会出现剩余的磁盘空间就够一个线程使用(假如是线程A ),其他线程已经写完了该阶段的内容(假如是线程B,C,D),开始执行删除操作了,此时,这4个线程有的在写入数据,有的在删除数据,会导致线程A一直读到有剩余空间可以写入(因为其他线程在删除文件,腾出新的空间),这样到最后4K的时候,就出现了A 线程还在写,其他的线程都已经停止好久了。又浪费了好多时间。

  如何解决呢?我想到了使用线程的同步,A线程写完了就等其他线程,等到所有的线程都写完了,大家一起开始删除,等到大家都删除完成了,再一起开始下一个阶段的写入

  那该如何等待呢?

  我用了Object类的wait()和notifyAll()方法。

第三阶段:

  使用同步解决其他线程结束,就剩余一个线程写的问题,进一步的提高效率

代码

import java.util.HashMap;
import java.util.Map; /**
* 状态信息类
*/
public class Status {
private int finishCount = 0;
private int threadCount = 0;
// 存储每个线程的状态
private Map<Integer, Boolean> maps; public Status(int threadCount) {
this.maps = new HashMap<Integer, Boolean>();
this.threadCount = threadCount;
} //更新当前线程是否在等待状态,status = true表示已经在等待了
public synchronized void setStatus(int threadIndex, boolean statu) {
maps.put(threadIndex, statu);
} public boolean getStatus() {
boolean result = true;
for (Map.Entry<Integer, Boolean> entry : maps.entrySet()) {
result = result && entry.getValue();
}
return result;
} // 更新已经完成的线程个数(全部各个阶段执行完调用)
public synchronized void updateFinishCount() {
this.finishCount += 1;
} public int getFinishCount() {
return finishCount;
}
}
/**
* 观察线程
*/
public class Watcher extends Thread{ private int threadCount = 0;
private Status status; public Watcher(Status status,int threadCount ) {
this.status = status;
this.threadCount = threadCount;
} public void run() {
while(true){
//检查是否所有status对象上的线程是否都在等待
if(status.getStatus()){
status.notifyAll();
}
//检查是否所有线程全部执行完成
if(status.getFinishCount() == threadCount){
break;
}
}
}
}
/**
* 具体执行任务的线程
*/
public class Task extends Thread{
private Status status;
private int index;
public Task(Status status,int index) {
this.status = status;
this.index =index;
}
public void run() {
int size = 64;
while(size >=4){
write(size);
synchronized (status) {
status.setStatus(index, true);//设置为等待状态
try {
status.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
status.setStatus(index, false);//取消等待状态
delete();
synchronized (status) {
status.setStatus(index, true);//设置为等待状态
try {
status.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
status.setStatus(index, false);//取消等待状态
size /= 2;
}
} private void write(int size){
//省略具体的写数据方法,判断是否写满
} private void delete(){
//省略删除的方法
}
}

现在看起来是完美了,但是实际的运行过程中,会发现,真的没有控制住线程的同步,还是出现了之前的第二阶段的问题,有一个线程比其他线程慢,而且出现了一个线程没有按照依次递减的顺序执行的古怪情况,我想应该是没有真正的同步造成的。之后又去查找资料,发现Java提供了一个类,就像是为这种情况量身定做的。它就是 CyclicBarrier  ,它自己维护了一个计数器,每当调用一次await()方法,就会阻塞当前的线程,并且计数器减一,计数器的值来源于构造方法,计数器为0的时候,就解除阻塞,更好的是,当计数器为0时,再调用await()方法的时候,会将计数器变成初始值减一,重新开始一个循环。

第四阶段:

  

**
* 具体执行任务的线程
*/
public class Task extends Thread {
private CyclicBarrier cyclicBarrier; public Task(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
} public void run() {
int size = 64;
while (size >= 4) {
write(size);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
//捕获到该异常的话,表示这个线程不用等待了,需要处理一下,唤醒其他阻塞的线程
e.printStackTrace();
}
delete();
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
size /= 2;
}
} private void write(int size) {
// 省略具体的写数据方法,判断是否写满
} private void delete() {
// 省略删除的方法
}
}

测试的代码

public class Test {
private static final int THREAD_COUNT = 4;
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT);
for(int i=0;i<THREAD_COUNT;i++){
new Task(barrier).start();
}
}
}

  

最新文章

  1. php实现hack中的Shape特性
  2. 在 Linux 上配置一个 syslog 服务器
  3. Node.js文件系统、路径的操作详解
  4. 【c#搬砖记】用Docx导出word格式的docx文件
  5. Memcached深度分析
  6. Android核心分析之十八Android电话系统之RIL-Java
  7. 爬虫学习之基于Scrapy的网络爬虫
  8. Mysql软删除
  9. BFC——块级格式上下文
  10. Repeater绑定数据库,使用AspNetPager进行分页
  11. 带参数的存储过程和标量Function
  12. 关于MySQL latch争用深入分析与判断
  13. shell脚本中 杀死可能成为僵尸进程的方法
  14. HTML5中 基本用法及属性 韩俊强的博客
  15. Spring Boot整合Elasticsearch
  16. Spring Boot Docker 实战
  17. Delphi 中的 XMLDocument 类详解(10) - 判断节点类型: 支节点、叶节点、文本节点、空节点
  18. python绘制三维图
  19. git pull时冲突的几种解决方式
  20. opencv的基本数据类型CvPoint,CvSize,CvRect,CvScalar

热门文章

  1. centos文件查找命令
  2. 制作kvm镜像、格式转换
  3. 推荐-Everything搜索工具
  4. redis 网络库文件 重构
  5. Image和Base64相互转换
  6. (转)tune2fs命令详解
  7. Oracle Schema
  8. 带双反斜杠的Json数据至单反斜杠的Json数据处理
  9. hibernate的中的查询与级联操作
  10. MySQL之函数