发表于: 2018-03-08 23:58:01

1 542


今天完成的事情:

1. 学习spring 的多线程

2. 尝试解决多线程读取文件

明天计划的事情

1. 回学校,面试


遇到的问题:


收获:

1. 学习spring 的多线程和变量的内容共享

xml配置

<!-- spring 线程池 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
   <!-- 最小线程数 -->
   <property name="corePoolSize" value="10"/>
   <!-- 最大线程数 -->
   <property name="maxPoolSize" value="100"/>
   <!-- 最大队列长度 -->
   <property name="queueCapacity" value="1000"/>
   <!-- 线程池维护线程所允许的空闲时间 -->
   <property name="keepAliveSeconds" value="300"/>

   <!--<property name="waitForTasksToCompleteOnShutdown" value="true"/>-->
   <!--<property name="awaitTerminationSeconds" value="20"/>-->
   <!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
   <property name="rejectedExecutionHandler" >
       <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
   </property>
</bean>


多线程的类

public class Task implements Runnable  {

private  Integer line = 0;
   public ArrayList<Integer> lines = new ArrayList<>(200);
   
   @Override
   public void run(){

      synchronized(line){
                      line = line + 1;
           lines.add(line);
       }


       try {
           Thread.sleep(10);
       } catch (InterruptedException e) {
            e.printStackTrace();
       }
   }
}



测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-thread.xml"})
public class Runadle {

@Autowired
   private ThreadPoolTaskExecutor taskExecutor;

   @Test
   public void test1() throws  Exception{
       Task task =  new Task();
       Long begin = System.currentTimeMillis();
       for(int i = 0; i < 200 ; i++){
          taskExecutor.execute(task);
       }
       taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
       taskExecutor.setAwaitTerminationSeconds(1)
;

       taskExecutor.shutdown();

       System.out.println(System.currentTimeMillis() - begin);
       HashSet<Integer> hashSet = new HashSet<Integer>();
       for (int i = 0; i < task.lines.size(); i++) {
          hashSet.add(task.lines.get(i));
       }
       System.out.println(hashSet.size());
   }

}


注意红色字体:

在网上说taskExecutor的

shutdown()

awaitTermination(2, TimeUnit.SECONDS);


这两个方法配合使用可以等多线程结束后再执行下面的代码,

但是spring 4.3.9版本没有awaitTermination(2, TimeUnit.SECONDS)方法

只有单独一个shutdown()方法,虽然他的源码里调用了awaitTermination(2, TimeUnit.SECONDS)方法但是不起作用


经研究后发现需要用红字的方式,或者在xml里配置相关内容才能用shutdown()等待完成多线程任务。

为灵活起见就没有配置在xml里面。


上面的黄字内容是为了保证变量在不同线程之间的数据的共享的符合预期的。

即每启动一个线程line+1 下一个线程在这个的基础上+1,保证其原子性


解决方案就是通过 synchronized 锁这部分代码块

为什么不用 volatile  因为 它只能保证线程之间每次读取该变量的时候是从主内存里读取最新值,而当该变量发生修改变化时,也会强迫线程将最新的值刷新回主内存中

但是volatile不能保证变量更改的原子性:

例如: line = line + 1

一共有三个步骤

1. 读取line 

2. line + 1

3. 写回line


如果两个线程A,B

A进行到步骤2时候,B开始执行,就读取到了没有改变的值。所以不能这样写,可能线程交叉执行


通过 synchronized 来保证这3步都是锁内操作原子性的。


通过HashSet来验证是否有重复数据:

将line的值都写入一个hashSet,循环200次,则HashSet的长度应为200 

上面的为所有线程池里的任务执行完后消耗的时间

下面的是hashset的长度。



2. 尝试解决多线程读取文件

1. 方案为获取文件的长度,分成合适的几段,以第一段的长度为偏移量,向后读取一部分,直到换行符。

将从开头到这里作为第一段偏移量记录下来,然后以这个偏移量为基础,计算第二段。。。。直到最后一段。


然后按照记录下来的偏移量,多线程分段读取并处理。


技术难点:  1. 变量线程之间的安全共享(已解决)

                   2. 找到偏移量后面的第一个换行符的位置(待解决)






返回列表 返回列表
评论

    分享到