题目描述:
给定一个队列A[1-10][1-100000]、元素编号1-10,其中每个元素包含10万个随机数。创建若干个线程,各循环100次;其中10,100个为读线程,10,100个为更新线程。
1. 读线程每次产生随机数三元组:(i, j, k),其中i:[1-10], j: [1-100000],k[j-100000],求取指定A[i]一级数组中第j到第k个元素的均值;
2. 更新线程每次产生一对随机整数三元组 (i, j, k) 和一个随机浮点数d:(0-1),其中i: [1-10], j: [1-100000],k[j-100000],完成对A[i][j]到A[i][k]之间的所有元素进行数值更新,x = x * (1 + d)
基础知识:
读者—写者问题(Readers-Writers problem)也是一个经典的并发程序设计问题,是经常出现的一种同步问题。计算机系统中的数据(文件、记录)常被多个进程共享,但其中某些进程可能只要求读数据(称为读者Reader);另一些进程则要求修改数据(称为写者Writer)。就共享数据而言,Reader和Writer是两组并发进程共享一组数据区,通常要求满足:
(1)允许多个读者同时执行读操作;
(2)不允许读者、写者同时操作;
(3)不允许多个写者同时操作。
实现的原理:
Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。
Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用Semaphore可以实现有限大小的链表。另外重入锁 ReentrantLock 也可以实现该功能,但实现上要复杂些。
编程实现:
工程下载地址:http://download.youkuaiyun.com/detail/qq_24369113/9720663
ReadAndWrite.java是测试类
ReadThread.java是读线程类
WriteThread.java是写线程类
默认生成100个读线程和100个写线程,每个线程循环100次,ReadAndWrite.java中的Value数组模拟临界区资源,对其每一行设置一个锁,对每一行允许同时最多有5个读线程进行操作,或者一个写线程进行操作。
ReadThread.java
import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.Semaphore; /** * Created by 32706 on 2016/12/24. */ public class ReadThread extends Thread { public int id;// 读者ID //public static int readCount=0;// 读者数量 public static Map<Integer,Integer> readCountMap=new HashMap<>();//存放每一行的读者数量 public static Map<Integer,Semaphore> mutex=new HashMap<>();//确保在更新readCountMap的原子性 public static void init_readthread_readcountmap() { for(int i=0;i<ReadAndWrite.Value.length;i++) { readCountMap.put(i,0); mutex.put(i,new Semaphore(1)); } } public ReadThread(int id) { this.id = id; } private double get_avrage(int i,int j,int k) { double sum=0; for(int r=j;r<=k;r++) sum=ReadAndWrite.Value[i][r]+sum; return sum/(k-j+1); } //读者优先 public void run() { try{ for(int x=0;x<ReadAndWrite.execute_times;x++) { int thread_i=(int)(Math.random()*20)%10; int thread_j=(int) (Math.random()*200000)%100000; int thread_k=(int)(Math.random()*200000)%(100000-thread_j)+thread_j; try { System.out.println(id+"号读线程准备读取第"+thread_i+"行的"+thread_j+"=>"+thread_k+"的数据"+" "+ System.currentTimeMillis()); mutex.get(thread_i).acquire(); if (readCountMap.get(thread_i) == 0)//如果是第i行的第一个读者,那么要考虑是否有写者,没有写者,直接读,有写者,等待写者 { if(ReadAndWrite.writeSemaphoreMap.get(thread_i).availablePermits()==0)//有人在写 { System.out.println("有写线程在写操作,"+id+"号线程等待读===="+" "+ System.currentTimeMillis()); } ReadAndWrite.writeSemaphoreMap.get(thread_i).acquire();//上写锁 } int readCount=readCountMap.get(thread_i)+1;// 已经具备读的条件了,读者数量加1 readCountMap.put(thread_i,readCount); mutex.get(thread_i).release(); if(ReadAndWrite.readCountSemaphoreMap.get(thread_i).availablePermits()==0)//读者过多 { System.out.println("当前读线程过多,"+id+"号线程等待读===="+" "+ System.currentTimeMillis()); } // 等待着读 ReadAndWrite.readCountSemaphoreMap.get(thread_i).acquire();//如果读者过多则会等待,读者小于5则可以读 //可以读了 System.out.println(id+"号读线程正在读取数据...."+" "+ System.currentTimeMillis()); System.out.println(id+"号读线程计算结果为~~~~"+get_avrage(thread_i,thread_j,thread_k)+" "+ System.currentTimeMillis()); mutex.get(thread_i).acquire(); //读完了,读者数量减少1 int readCounttem=readCountMap.get(thread_i)-1; readCountMap.put(thread_i,readCounttem); if(readCountMap.get(thread_i)==0)//没有读者了,可以写了 { ReadAndWrite.writeSemaphoreMap.get(thread_i).release(); } ReadAndWrite.readCountSemaphoreMap.get(thread_i).release();//释放读者信号量 mutex.get(thread_i).release(); Thread.sleep((long) (new Random().nextFloat()*1000)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } finally{ ReadAndWrite.Runningthread++; } } }