第一题:两个数集,都有20亿个数,4G内存,可以使用外存,求两个数集的交集
第二题:原题就是这样的代码,问高并发环境存在的问题,如何改进public class CountServlet extends HttpServlet
{
private volatile static int count;

public void processRequest(HttpServletRequest req)
{
//processRequest
}

public int getCount()
{
return count;
}


public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
count++;
processRequest(req);
PrintWriter out = resp.getWriter();
out.write("hello");

}
}

解决方案 »

  1.   

    第一个用输入流
    第二个,把count改为非static的。请指教
      

  2.   

    还没理解过来 为啥要改成非static的 volatile不太了解 他是只跟成员变量起作用么
      

  3.   

    第一题不知道 单说第二题虽然他用了volatile 修饰符 但是这个修饰符仅提供内存可见性保证,不提供原子性。所以在更新属性count的时候 还是要用synchronized代码块的
    另外 这个属性是不是静态在并发这里并不重要
      

  4.   

    关于第一题:
    既然提到4G内存了,两个20亿的数据集。
    那就将这两个数据集进行拆分,分为较小的数据集。
    然后再利用HashSet之类的东西存储遍历的其中一个数据集,然后再遍历第二个,进行判断~ ······
      

  5.   


    volatile是原子性的吧,请指教,Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
      

  6.   

    同意14楼观点
    正确使用 volatile 变量的条件
      您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
      ● 对变量的写操作不依赖于当前值。
      ● 该变量没有包含在具有其他变量的不变式中。
      实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
      第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。)
      大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized 那样普遍适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。
    baidu了一下,lz的代码不符合使用volatile 的条件,因为lz代码显然是记访问量的。
    所以最后还是要使用synchronized
      

  7.   


    http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
    看这个
    对了 补充下 用AtomicInteger也可以
      

  8.   

    IBM那个说明以前倒是看过,平时用的少,没理解过来
    刚才zhao251021539私底下跟我说volatile都是从主内存中读写操作,大致有些了解了
    以后有时间再看看去第一题这种方法昨天是这么想的,但是感觉好麻烦的说,比如分成N块,那就是要比较N^2次,比完了还得去交集中的重复部分
      

  9.   

    我基本不用 所以我第一次回答的时候跟huxiweng、zhao251021539回答的一致(当然 没那么深入 就是那种盲目的认为...)
      

  10.   

    刚才看书的时候发现一段话 摘录如下原子性(原子性操作是不能被线程调度机制中断的操作)可以应用于除long和double之外的所有基本类型之上的"简单操作(我个人理解简单操作就是单纯的赋值,但是在赋值过程如果依赖运算的话就不安全了,具体可以看我发的文章链接)"。对于读取和写入除long和double之外的基本类型变量这样的操作,可以保证它们会被当做不可分(原子)的操作来操作内存。但是JVM可以将64位(long和double)的读取和写入当做两个分离的32位操作来执行,这就产生了在一个读取和写入操作中间发生上下文切换,从而导致不同的任务可以看到不正确结果的可能性(这有时被称为字撕裂,因为你可能会看到部分被修改的数值)。但是,当你定义long或者double变量时,如果使用volatile关键字,就会获得(简单的赋值与返回操作的)原子性(注意,在JavaSE5之前 volatile一直未能正确工作)。不同的JVM可以任意的提供更强的保证,但是你不应该依赖于平台相关的特性。摘自Think in java 第四版中文 第二十一章 并发 21.3.3原子性与易变性
      

  11.   

    我说一下第一种吧,假设都是int
    内存中开一个byte[256][256][256][32],内存消耗250M(按照1000算,按1024算更小)
    每个整数占一个字(bit),
    比如:1,000,000,000,十六进制表示为3B9ACA00,
    那么在这个byte数组中的位置就是bytes[0x3B]b[0x9A][0xCA][0x00]的第1位(右面第一个bit)如果此bit为0,则表示之前没有1,000,000,000,如果此bit为1,则说明出现过比如:1,000,000,十六进制表示为000F4240,
    那么在这个byte数组中的位置就是bytes[0x00]b[0x0F][0x42][0x08]的第1位如果此bit为0,则表示之前没有1,000,000,如果此bit为1,则说明出现过相关公式(不考虑负数,如有负数,考虑使用>>>
    bytes[x >> 24][(x >> 16) & 0xFF][(x >> 8) & 0xFF][(x >> 3) % 32],每一个byte的计算:
    bytes[x >> 24][(x >> 16) & 0xFF][(x >> 8) & 0xFF][(x >> 3) % 32] |= 1 << (x % 8);
      

  12.   

    只写了个演示的代码
      public static void main(String[] args) throws Exception {
        byte[][][][] bytes = new byte[256][256][256][32];
        for (int x = 0; x < 2000000; x++) {
          bytes[x >> 24][(x >> 16) & 0xFF][(x >> 8) & 0xFF][(x >> 3) % 32] |= 1 << (x % 8);
          System.out
              .printf("%d: bytes[%d][%d][%d][%d] = %x %n",
                      x,
                      x >> 24,
                      (x >> 16) & 0xFF,
                      (x >> 8) & 0xFF,
                      (x >> 3) % 32,
                      bytes[x >> 24][(x >> 16) & 0xFF][(x >> 8) & 0xFF][(x >> 3) % 32]);
        }
      }
    -Xmx2048m 就可以跑了
      

  13.   

    先把第一个数集按照如上方法存入,然后遍历第二个数集
    要判断1993480是否存在的话,那么就去bytes[0][30][107][1],判断右面第一位(1993480 % 8 = 0)。
    if ((bytes[x >> 24][(x >> 16) & 0xFF][(x >> 8) & 0xFF][(x >> 3) % 32] | (1 << (x % 8)))!=0)如果这个数组保存的结果是0xF0或者0x28,那么说明右面第一位是0,那么1993480不存在
    如果这个数组保存的结果是0xFF或者0x21,那么说明右面第一位是1,那么1993480存在
      

  14.   

    我这个没范围,只要每个数是Integer即可
      

  15.   

    256×256×256×32×8(bits per byte)已经是Integer的全集了
      

  16.   

    不太可能说每个数都是123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890吧。一般海量数据,数的个数没限制,但是每个数最多是个int(long都很少)
      

  17.   

    另外,如果没有那么大的内存的话,可以把byte[][][][]转化成RandomAccessFile[]
      

  18.   

    这里有个小问题,忘记说了,前面说过byte[][][][],256*256*256*32bytes只有250M,其实不对
    byte[256][256][256][32]除了256*256*256*32bytes外,还有
    256个byte[][][]数组
    256×256个byte[][]
    ....
    所以我一开始设置-Xmx800m还不够不过,如果直接byte[256*256*256*32]的话,内存消耗小了,但是貌似访问速度会慢很多。具体如何权衡,没有tuning过
      

  19.   

    http://topic.csdn.net/u/20111011/19/4ad9eb4c-c9ec-45d4-88d1-40e7eef8e011.html
    这个地方不太了解,还请坦克前辈再指点下~
      

  20.   


    请你搜索下容器启动后Servlet会有几个实例...
      

  21.   


    不是默认是单例 跟单例无关..遵循规范的容器只创建一个实例(但不是那个什么单例 因为你要是乐意的话你可以自己new几个Servlet玩)
    不遵循的就可能创建多个实例(我也不知道 乱喷的)
      

  22.   

    第一题因为有4G内存,可以用位图,位图存INTEGER只需要512M.
    第二题应该用原子INT.