在路上

路上有惊慌,路上有理想

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  28 Posts :: 1 Stories :: 10 Comments :: 0 Trackbacks

2010年10月28日 #

1.linux安装字体
   以微软雅黑为例,找到msyh.ttf ,copy至下面的文件夹
   usr/share/fonts/msyh
  执行命令:fc-cache -fv
  重启jvm即可
2.drawString 部分代码
private static BufferedImage drawString(int type, boolean isWhite,
            int width, int height, String price, Font font_money, Font font,
            Graphics2D g2d, Rectangle2D bounds, Rectangle2D bounds_money) {
         BufferedImage image;
         //透明背景
          image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);  
         g2d.dispose();  
         g2d = image.createGraphics();  
         //反锯齿字体
         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
         if(!isWhite){
             //非白字
             g2d.setColor(new Color(236,0,137));  
         }else{
             //白字
             g2d.setColor(new Color(255,255,255));  
         }
         //字体居中
         double y = (height - bounds.getHeight()) / 2;      
         double ascent = -bounds.getY();      
         double baseY = y + ascent;      

         g2d.setStroke(new BasicStroke(1));  
         g2d.setFont(font_money);
         g2d.drawString(FONT_RMB_CHAR, -2, (int)baseY);  
         g2d.setFont(font);
         g2d.drawString(price, (int)bounds_money.getWidth()-4, (int)baseY);
         g2d.dispose();
         return image;
    }
3.如果需要根据字符串的长度生成图片的宽度,可以使用如下方法
 Rectangle2D bounds = font.getStringBounds(price, context);
 width = (int)(bounds.getWidth();

4.批量生成,使用java自带的线程池,并使用CompletionService,目的是在线程处理结束后得到生成成功的ProductId
      public boolean generateImagesBatch(){
          boolean flag=true;
          ExecutorService exec = Executors.newFixedThreadPool(8);
           CompletionService<CallBack> completionService=
                    new ExecutorCompletionService<CallBack>(exec);
          long startTime=System.currentTimeMillis();
              String sql="select productId,price from prod";
            List<Map> skuList = this.cmsJdbcTemplate.queryForList(sql);
            for(Map map:skuList){
                String prodId=((BigDecimal)map.get("productId")).toString();
                double price=((BigDecimal)map.get("price")).doubleValue();
                completionService.submit(new CreateImageConcurrent(prodId,price,FontEnum.ONE,false));               
                completionService.submit(new CreateImageConcurrent(prodId,price,FontEnum.TWO,false));            }
            long endTime=System.currentTimeMillis()-startTime;
            log.info("query db time>>>>>>>>>>>>>>"+endTime/1000);
            
            Future<CallBack> future;
            int count=skuList.size()*6;
            log.info("generateImagesBatch count:"+count);
            try {
                while(count>0){
                    future = completionService.take();
                    CallBack callBack = future.get();
                    if(null!=callBack){
                        count--; log.info("generateImagesBatch prod id:"+callBack.getSuccesMessage());                    }
                }
            endTime=System.currentTimeMillis()-startTime;
            log.info("create images time>>>>>>>>>>>>>>"+endTime/1000);
            log.info("generateImagesBatch success!");
            flag=true;
            } catch (ExecutionException e) {
                flag=false;
                log.error("generateImagesBatch fail::ExecutionException::"+e.getMessage());
            } catch (InterruptedException e) {
                flag=false;
                log.error("generateImagesBatch fail::InterruptedException::"+e.getMessage());
            }finally{
                exec.shutdown();
            }
            return flag;
      }
posted @ 2012-04-18 11:35 阮步兵 阅读(1839) | 评论 (0)编辑 收藏

以下只是一些概念


1.SemaphoreCountDownLatch
 
Semaphore 可用于控制特定资源请求(线程/操作)数量
 
CountDownLatch 在功能上类似于Semaphore。区别是,Semaphore允许一次一个线程的话,CountDownLatch可以允许多个线程在特定的时间一起执行。

2.CAS操作
  根据英文直译其实可以理解其含义,compare and set.

  CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。CAS 认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。

3.ABA问题

  因为在更改 V 之前,CAS 主要询问“V 的值是否仍为 A”,所以在第一次读取 V 以及对 V 执行 CAS 操作之前,如果将值从 A 改为 B,然后再改回 A,会使基于 CAS 的算法混乱。在这种情况下,CAS 操作会成功,但是在一些情况下,结果可能不是您所预期的。这类问题称为 ABA 问题

4.原子操作

   A与B两个操作。从执行A的线程看,当其他线程执行B时,要么B全部执行完成,要么一点都不执行。这样A与B互为原子操作。要保证数据状态的一致性,要在单一的原子操作中更新所有相关联的状态。

5.可见性
 
   在单线程环境下,读写操作都在一个线程内完成,不存在可见性问题。但是,当读与写操作不在同一个线程内时,就需要有可见性的要求——即可变的共享变量对所有线程都是可见的。

6.重排序

    JVM实现中,线程内部维持顺序化语义。如果程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。这个过程通过叫做指令的重排序。比如Java存储模型允许编译器重排序操作指令,在寄存器中缓存数值,还允许CPU重排序,并在处理器的缓存中缓存数值。

   当然,在没有同步的多线程情况下,编译器,处理器,运行时安排操作的执行顺序可能完全出人意料。

7.内部锁

  每个Java对象都可以隐士的扮演一个用于同步的锁的角色。比如synchronized(object){},执行线程进入synchronized块 之前自动获得锁。无论是正确执行或是抛出异常,最终都会释放该锁。内部锁是一种互斥锁(mutex)——至多只有一个线程可以拥有锁。JDK中有该锁的实 现。

8.锁与内存

   锁不仅仅是关于同步与互斥,也是关于内存可见性的。为了保证所有线程都能访问共享变量的最新值,读和写的线程必须使用公用的锁进行同步。

9.锁与volatile

   加锁可以保证可见性与原子性,volatile只能保证可见性。

10.happen-before法则

  Java存储模型有一个happens-before原则,就是如果动作B要看到动作A的执行结果(无论A/B是否在同一个线程里面执行),那么A/B就需要满足happens-before关系。比如一个对象构造函数的结束happens-before与该对象的finalizer的开始


参考:https://www.ibm.com/developerworks/cn/java/j-jtp11234/

     http://www.ibm.com/developerworks/cn/java/j-5things5.html

     http://www.blogjava.net/xylz/archive/2010/07/03/325168.html

    《Java 并发编程实践》

posted @ 2010-12-15 18:18 阮步兵 阅读(1721) | 评论 (0)编辑 收藏

BTrace是一个实时监控工具,使用了java agent jvm attach技术,可以在product的情况下实时监控线上程序的运行情况。另,有插件可与visualVM一起使用。
不多说了,具体的可见:http://kenai.com/projects/btrace

下面介绍几个Helloworld示例:
主要使用了btrace命令:btrace [pid] class

pid可由jps命令快速查询

1.监控方法输入参数:
 @OnMethod(
                clazz="com.btrace.Person",
                method="/set.*/"
            )
            public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
                println(pcn);
                println(pmn);
                printArray(args);
            }

执行btract命令
后台输出:
com.btrace.Person
setId
[1, ]……

2.监控方法返回值
  @OnMethod(
                 clazz="com.btrace.Person",
                 method="/get.*/",
                 location=@Location(Kind.RETURN)
               )  
     public static void defineclass(@Return String cl) {
           println(Strings.strcat("getValue ", cl));
           Threads.jstack();
       }
执行btract命令
后台输出:
getValue gum
com.btrace.TestThread.main(TestThread.java:23)

3.监控jvm内存使用情况
  @OnTimer(4000)
    public static void printMem() {
        println("Heap:");
        println(heapUsage());
        println("Non-Heap:");
        println(nonHeapUsage());
    }
执行btract命令
后台输出:
Heap:
init = 268435456(262144K) used = 26175176(25561K) committed = 251658240(245760K)
 max = 492175360(480640K)
Non-Heap:
init = 12746752(12448K) used = 5892104(5754K) committed = 13598720(13280K) max =
 100663296(98304K)
4.监控方法执行时间
   @TLS private static long startTime;
   
    @OnMethod(clazz="com.btrace.Person",method="setId")
    public static void onCall(){
        println("enter this method");
        startTime=timeMillis();
    }
   
    @OnMethod(clazz="com.btrace.Person",method="setId",location=@Location(Kind.RETURN))
    public static void onReturn(){
        println("method end!");
        println(strcat("Time taken ms",str(timeMillis()-startTime)));
    }
  后台输出:
   enter this method
   method end!
   Time taken ms0
5.监控Thread start
 @OnMethod(
        clazz="java.lang.Thread",
        method="start"
    )
    public static void func() {
        println("about to start a thread!");
    }
后台输出:about to start a thread!
posted @ 2010-12-10 18:30 阮步兵 阅读(3734) | 评论 (1)编辑 收藏

     摘要: 1.注意auto_increment mysql5.0在高并发下(每天近2亿)插入单表数据出现了死锁(偶现),查了下相关文档,发现是因为采用了auto-increment的主键带来的问题,造成Table级的死锁。 原因:对于auto_increment字段,innodb会在内存里保存一个计数器用来记录auto_increment的值,当插入一个新行数据时,就会用一个表锁来锁住这个计数器,所以会...  阅读全文
posted @ 2010-12-09 12:56 阮步兵 阅读(1636) | 评论 (2)编辑 收藏

一.限制设计——从结构上说,是利用封装技术,保证某一时刻只有一个活动访问某个对象。

方式主要三类,方法限制、线程限制和对象内限制

方法限制:

   1.方法内部限制:采用局部变量方式

   2.方法间传递限制:

         a.调用者copy:比如print(p) 可以改为print(new Point(p));

         b.接收者copy:Point p=new Point(p.x,p.y);

         c.标量参数:print(int x,int y);d.print(p.x,p.y);

线程限制:

     1.最简单的方法是将所有可变对象都放在一个线程内执行

               public display(){

                         new Thread(){

                                  public void run(){//do something here}

                      }.start()

                }

      2.线程私有成员变量

         最直接的办法是利用现有类:ThreadLocal.

        当然你可以利用Thread.currentThread()自己实现一个类似功能的类,但Thread.currentThread有限制,就是对特定线程的一类。

        而ThreadLocal则摆脱了这样的限制。而且在线程内对ThreadLocal私有变量的读写不需要同步。

对象限制

       在前面两种方法都不能做到对对象的限制访问时,你就不得不使用锁。但同时,也可以对对象内部及不同部分的访问进行结构上的限制。

     1.适配器模式

      比如 class Point{

                 public double x;

                 public double y;

                 public synchronized double getX(){};

                //……

      }

    采用对象限制的设计方式,会将synchronized 锁移除到一个其他对象里,这样就解脱了Point.

     like this

           class SychPoint {

                 private final Point point=new Point();

                public synchronized double getX(){point.x}

         }

    class Point{

                 public double x;

                 public double y;

                 public double getX(){};

      }

    说白了就是采用适配器模式,改变了一下原来类的结构。java.util.Collection framework 里面就是使用这种策略组织起集合类的同步。

   2.子类化

       将锁延迟到子类实现,这里将不再罗嗦。

二.同步设计

     使用锁的注意事项

       1.有些入口锁在只有少数线程访问的情况下,可以很好的工作,开销并不大。但是当并发量变大,竞争加剧,开销也变大,系统的性能会随之下降。大多数线程会把大部分时间浪费在等待上。系统出现了延迟,限制了并发系统的优越性。

       2.使用太多的锁,会增加系统负担,以及不可料的情况发生,比如死锁。

       3.只用一把锁来保护一个功能的多个方面会导致资源竞争。

       4.长时间持有锁,会带来性能问题和异常处理的复杂。

       5.有时候加锁并不一定能保证得到我们想要的结果。

    对付以上这些问题,没有什么最佳策略,大都需要去权衡各个方面的利弊来进行设计。写多线程的程序,前期的设计比后期维护更为重要。

    初期的设计原则,

        1.减少同步

             a.用户可以接受陈旧数据,可以拿掉同步,使用volatile

             b.用户在得到非法数据时,只要能得到提示就够了,可以使用double-check方法。

                在不同步时check一次,再在同步状态在check一次。这么做的意义在于缩小锁使用范围,在第一次check不满足的情况,跳出方法,那么锁也就用不到了。

             c.只对维护状态部分加锁:当对象的某个同步方法是比较耗时的操作,那么锁持有的时间就越长,而仅仅是为了保持一个状态是,可以采用openCall的方式,减少持有锁时间。

                              public sychronized void updateState(){}

                              public void readFile(){

                                      updateState();//持有锁

                                    file.read();

                               }

               如上,这种方式的前提是程序不需要同步一个方法中无状态的部分。如果整个方法都需要锁,那这种方式就不适用了.

            D.能使用同步块,就不需同步整个方法。

     2.分解同步:

        分解类

            将锁拆分到辅助类中

        分解锁

           如果不愿分解类,可以设计分解锁

                    private static Object lock1 = new Object();

                   private static Object  lock2 = new Object();

                  synchronize(lock1){}

                  synchronized(lock2){}

              在jdk 5.0之后的并发包里,已有可重入锁供使用。

          隔离成员变量

              Person的age,income等属性都需要同步处理,以保证并发修改时,可以设计一些同步的int,Double等类型(util.concurrent已提供类似的类),将锁交给辅助类去处理。起到隔离作用.

 

posted @ 2010-11-11 17:47 阮步兵 阅读(1723) | 评论 (0)编辑 收藏

Java 内存模型

JVM系统中存在一个主内存(Main Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

模型的规则:

1.原子性:保证程序得到成员变量(非局部变量)的值或者是初始值,又或者是某线程修改后的,绝对不是多个线程混乱修改后的。

2.可见性(共享内存的数据):什么情况下,写入成员变量的值对读取该变量的值是可见的?

     A.写操作释放了同步锁,读操作获得了同步锁

           原理:释放锁的时候强制线程把所使用的工作内存中的值刷新到主存,获得锁的时候从主存重新装载值。

           p.s.锁只被同步块和方法中的操作占有,但却控制了执行该操作的线程的所有成员变量。

     B.如果一个成员变量为volatile,那么在写线程做存储操作前,写入这个成员变量的数据会在主存中刷新,并对其他线程可见。读线程每次使用这个成员变量前都要重新从主存读数据。

     C.如果一个线程访问一个对象的成员变量,读到的值为初始值或者另一个线程修改后的值。

        p.s. 不要对引用未完全创建好的对象。

               如果一个类可以被子类化,那么在构造函数里启动一个线程是非常危险的

     D.当一个线程结束后,所有的写入数据都会被刷新到主存。

          p.s.同一个线程的不同方法之间传递对象的引用,永远不会有可见性问题

   存储模型保证:如果上面的操作都会发生,那么一个线程对一个成员变量的更新最终对另一个线程是可见的。

3.顺序化(内存操作的顺序):什么情况下,一个线程的操作可以是无序的?顺序化的问题主要围绕和读写有关的赋值语句的执行顺序。

   如果采用同步机制,那不用多说,顺序化可以保证。

   当没有同步机制时,存储模型所做的保证是难以相信的。在多线程环境下,存储模型是难以保证一定正确的。

  只有当满足下面的三个原则,顺序化才得以保证。

   A.从线程执行方法的角度看,如果指令都是串行执行的,那么顺序可以保证

   B.保证同步方法或块的顺序执行

   C.使用volatile定义成员变量

线程执行过程中,存储模型与锁的关系:

(1) 获取对象的锁

(2) 清空工作内存数据, 从主存复制变量到当前工作内存, 即同步数据

(3) 执行代码,改变共享变量值

(4) 将工作内存数据刷回主存

(5) 释放对象的锁

最后介绍一下volatile关键字

     volatile定义的成员变量可以保证可见性和顺序化,但不保证原子性。比如count++。
     *比如把一个变量声明为volatile,并不能保证这个变量引用的非volatile数据的可见性。比如volatile string[10](数组)

     正确使用volatile的前提条件

     a.对变量的写操作不依赖于当前值

     b.不要和其他成员变量遵守不变约束。见*处的解释

    volatile的应用

     a.状态标志

        volatile boolean shutdownFlag;

       public void shutdown() { shutdownFlag= true; }
       public void doWork() {
       while (!shutdownFlag) {
        // do something
         }

     b.假设一个后台线程可能会每隔几秒读取一次数据库里的合同金额,并更新至 volatile 变量。然后,其他线程可以读取这个变量,从而随时能够看到最新的金额。 比较广泛应用在统计类的系统中。

参考文档:

http://www.cs.umd.edu/~pugh/java/memoryModel/

http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

《Java并发编程:设计原则与模式》

posted @ 2010-11-03 17:56 阮步兵 阅读(1434) | 评论 (0)编辑 收藏

1.迭代问题

  多线程环境下,迭代容易引起问题,如

  for(int i=0;i<v.size();i++){System.out.println(v.get(i))}

 解决办法之一:

     客户端加锁

     for(int i=0;true;i++){

                   Sychronzied(v){

                            if(i<v.size()){

                                 System.out.println(v.get(i)

                             }

               }

},当然这种也有问题,一旦程序可以重新设置元素位置,也会出错。

幸好有一种比较安全的办法: copy遍历对象

   Sychronzied(v){

                        Object v_clone= copy(v);

               }

     iterateV(v_clone);

2.Singleton

   单例习惯的方式,采用延时初始化,

   public static A getInstance(){

                   if(null==instance){

                     instance=new A();

                }

             return instance

   }

  在多线程模式下,需要加锁,来保证同步Sychronized(object){}。

如果初始化对象并不占用太多资源,其实没有必要加锁,毕竟同步也是很耗资源的。取消延时初始化,priavte static final instance=new A();

3.顺序化资源

   顺序化资源是避免死锁的简单的方式。

   死锁:T1时间,线程A 拥有objA的锁,请求objB的锁。线程B拥有objB的锁,请求objA的锁。

 如: System.identityHashCode(objA)<System.identityHashCode(objB)

 或者:public sychronized add(A a){sychronized(a){//do something}}

4.wait and notify

为了防止等待-通知机制出现race condition,需要加sychronized

race condition:objA在被wait之前已经被另一线程objB 给notify 了, 之后的wait 会永久停止,并导致deadlock(死锁),当然,如果你确认可以控制wait-notify很好,就不需要加了

posted @ 2010-10-28 11:45 阮步兵 阅读(1981) | 评论 (0)编辑 收藏