Java 多线程使用

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

问题:

   对磁盘进行碎片化测试(比如说,磁盘空间是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();
         }
    }
}

  

时间: 2024-10-10 15:51:44

Java 多线程使用的相关文章

Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3534050.html Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可

从JAVA多线程理解到集群分布式和网络设计的浅析

对于JAVA多线程的应用非常广泛,现在的系统没有多线程几乎什么也做不了,很多时候我们在何种场合如何应用多线程成为一种首先需要选择的问题,另外关于java多线程的知识也是非常的多,本文中先介绍和说明一些常用的,在后续文章中如果有必要再说明更加复杂的吧,本文主要说明多线程的一下几个内容: 1.在应用开发中什么时候选择多线程? 2.多线程应该注意些什么? 3.状态转换控制,如何解决死锁? 4.如何设计一个具有可扩展性的多线程处理器? 5.多线程联想:在多主机下的扩展-集群? 6.WEB应用的多线程以及

java多线程心得

多并发的时候,在什么情况下必须加锁?如果不加锁会产生什么样的后果. 加锁的场景跟java的new thread和Runnable的关系是什么? 看看java的concurrentMap源码. 还有spring 的web.xml启动执行源码 spring aop http://www.cnblogs.com/FDROSE1001/p/3661895.html activemq的本质是什么? java的jms hibernate由配置文件映射到实体类的本质是什么? java反射 spring aop

Rhythmk 一步一步学 JAVA (21) JAVA 多线程

1.JAVA多线程简单示例 1.1 .Thread  集成接口 Runnable 1.2 .线程状态,可以通过  Thread.getState()获取线程状态: New (新创建) Runnable (可以运行) Blocked  (被阻塞) Waiting  (等待) Timed waiting (计时等待) Terminated  (被终止) ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

(转载)Java多线程入门理解

转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢?如果你觉得此文很简单,那推荐你看看Java并发包的的线程池(Java并发编程与技术内幕:线程池深入理解),或者看这个专栏:Java并发编程与技术内幕.你将会对Java里头的高并发场景下的线程有更加深刻的理解. 目录(?)[-] 一扩展javalangThread类 二实现javalan

Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

ReentrantLock介绍 ReentrantLock是一个可重入的互斥锁,又被称为"独占锁". 顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有:而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取.ReentrantLock分为"公平锁"和"非公平锁".它们的区别体现在获取锁的机制上是否公平."锁"是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在

synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解

本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁,源码剖析 第一部分:synchronized与static synchronized的差别 1.synchronized与static synchronized 的差别 synchronized是对类的当前实例进行加锁,防止其它线程同一时候訪问该类的该实例的全部synchronized块.注意这里

深入聊聊Java多线程

一.背景 在没有学习Java多线程以前,总觉得多线程是个很神秘的东西,只有那些大神才能驾驭,新年假期没事就来学习和了解一下Java的多线程,本篇博客我们就来从头说一下多线程到底是怎么回事. 二.概述 1.进程的概念 每一个正在运行的程序都是一个进程,它是系统进行资源分配和调用的独立单位.且 每一个进程都有自己的内存空间和系统资源. 2.线程的概念 是进程中的单个顺序控制流,是一条执行路径.每个进程都可以拥有一个或者多个线程.各个线程之间都共享所属的那个进程的内存空间和系统资源. 3.单线程和多线

Java多线程 2 线程的生命周期和状态控制

一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable). 注意:不能对已经启动的线程再次调用start()方法,否则会出现Java.lang.IllegalThreadStateException异常. 2.就绪状态 处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列(尽管是采用队列形式,事实上,把它

Java 多线程

多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了.主要包括: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的start()方法(t