多线程学习(九)

原子类

JAVASE5 引入了诸如:AtomicInteger、AtomicLong、AtomicRenference等原子性变量,他们提供下面形式的原子性条件更新:
boolean cpmpareAndSet(exceptedValue,updateValue);
在常规编程中很难看见,在涉及性能调优的时候就有用武之地了。

public class AtomicIntegerTest implements Runnable {
    private AtomicInteger a = new AtomicInteger(0);

    public int getVal() {
        return a.get();
    }

    public void incr() {
        a.addAndGet(2);
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            incr();
        }
    }

    public static void main(String[] args) {
        new Timer().schedule(new TimerTask() {

            @Override
            public void run() {
                System.out.println("timer running");
                System.exit(0);
            }
        }, 5000); //定义一个定时器 来终止这个程序

        ExecutorService exec = Executors.newCachedThreadPool();
        AtomicIntegerTest at = new AtomicIntegerTest();
        exec.execute(at);
        exec.shutdown();//停止线程的提交,线程执行完之后尽快退出
        while (true) {
            int a = at.getVal();
            if(a%2!=0){
                System.out.println("found a error val:"+a);
                
            }
        }
    }
}

Atomic类是用来构建 juc 中的类的,只有在特殊的情况下才在自己的代码中使用他们,通常依赖于锁要更安全一些。(要么是synchronized 关键字要么是显示的Lock锁)
1114580-20170508150929910-1353054956.png

1114580-20170508144931379-636259698.png

通过同步控制块,而不是对整个方法的进行同步,可以使多个任务访问对象的时间性能得到显著的提高。
下面是synchronized同步整个方法和同步代码块的性能比较

public class Pair {
    private int x;
    private int y;

    public Pair(int x, int y) {
        super();
        this.x = x;
        this.y = y;
    }

    public Pair() {
        this(0, 0);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void incrX() {
        x++;
    }

    public void incrY() {
        y++;
    }

    @SuppressWarnings("serial")
    public class NotEqualsException extends RuntimeException {

        @Override
        public String toString() {
            return "Pair X、Y not equals :pair:" + this;
        }

    }

    public void check() {
        if (x != y) {
            throw new NotEqualsException();
        }
    }

    @Override
    public synchronized String toString() {
        return "Pair [x=" + x + ", y=" + y + "]";
    }

}
public abstract class PairManager {
    /**
     * 记录check的次数
     */
    AtomicInteger checkTime = new AtomicInteger(0);

    protected Pair p = new Pair(); // 继承可得
    /**
     * 保存Pair对象
     */
    private List<Pair> storge = Collections.synchronizedList(new ArrayList<Pair>());

    public synchronized Pair getPair() {
        return new Pair(p.getX(), p.getY()); // 这里返回的是获取那一时刻的对象的副本
    }

    public void store(Pair p) {
        storge.add(p);
        /**
         * list线程不安全的原因: 一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成: 1. 在 Items[Size]
         * 的位置存放此元素; 2. 增大 Size 的值。 在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且
         * Size=1; 而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B
         * 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0
         * (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加
         * Size 的值。 那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于
         * 2。这就是“线程不安全”了。
         */
    }

    public abstract void increment();

    @Override
    public String toString() {
        return "PairManager [checkTime=" + checkTime + ", pX=" + p.getX() + ",pY=" + p.getY() + "]";
    }

}
public class PairManager1 extends PairManager {

    @Override
    public synchronized void increment() {
        // TODO Auto-generated method stub
        p.incrX();
        p.incrY();
        store(getPair());
    }

}
public class PairManager2 extends PairManager {

    @Override
    public void increment() {
        Pair temp;
        synchronized (this) {
            p.incrX();
            p.incrY();
            temp=getPair();
        }
        store(temp);
    }

}
public class PairManager3 extends PairManager {
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public  void increment() {
        // TODO Auto-generated method stub
        lock.lock();
        try{
            p.incrX();
            p.incrY();
            store(getPair());
        }finally {
            lock.unlock();
        }
    }

}
public class PairManager4 extends PairManager {
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void increment() {
        // TODO Auto-generated method stub
        Pair temp;
        lock.lock();
        try {
            p.incrX();
            p.incrY();
            temp = getPair();
        } finally {
            lock.unlock();
        }
        store(temp);
    }

}
public class PairManipulator implements Runnable {
    private PairManager pm;
    
    public PairManipulator(PairManager pm) {
        super();
        this.pm = pm;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            pm.increment();
            try {
                TimeUnit.MILLISECONDS.sleep(10); //这里加这段代码是为了不让循环执行的太快导致线程栈溢出!!
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}
public class PairChecker implements Runnable {
    private PairManager pm;

    public PairChecker(PairManager pm) {
        super();
        this.pm = pm;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            pm.checkTime.incrementAndGet();
            pm.getPair().check();
        }
    }

}
public class CriticalSection {
    static void test(PairManager p1, PairManager p2, PairManager p3, PairManager p4) {
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Thread t, Throwable e) {
                // TODO Auto-generated method stub
                System.out.println("Thread: " + t.getName() + " Exception:" + e);
            }
        });
        ExecutorService exec = Executors.newFixedThreadPool(8);
        PairManipulator pm1 = new PairManipulator(p1), pm2 = new PairManipulator(p2), pm3 = new PairManipulator(p3),
                pm4 = new PairManipulator(p4);
        PairChecker pc1 = new PairChecker(p1), pc2 = new PairChecker(p2), pc3 = new PairChecker(p3),
                pc4 = new PairChecker(p4);
        exec.execute(pm1);
        exec.execute(pm2);
        exec.execute(pm3);
        exec.execute(pm4);
        exec.execute(pc1);
        exec.execute(pc2);
        exec.execute(pc3);
        exec.execute(pc4);
        exec.shutdown();
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("p1:" + p1 + "\np2:" + p2 + "\np3:" + p3 + "\np4:" + p4);
        System.exit(0);
    }

    public static void main(String[] args) {
        PairManager p1 = new PairManager1(), p2 = new PairManager2(), p3 = new PairManager3(), p4 = new PairManager4();
        test(p1, p2, p3, p4);
    }
}

1114580-20170508180412082-632958208.png

以上图片的输出不是一定是这个趋势的 ,这样测试有点不准。建议p1与p2测试,p3与p4测试,这样大概准一些。(从输出可以看出 检查的次数越多,越代表锁占用的时间越少)

一般的电脑运行这个程序多半都会报 线程栈溢出
接下来做一下eclipse的jvm参数配置:(参数设置的很暴力)
1114580-20170508180652972-1505823592.png

比较 synchonized 整个方法和 synchonized 代码块的对比:
1114580-20170508185135832-905629126.png

大概可以看出 两个之间的性能

转载于:https://www.cnblogs.com/joeCqupt/p/6825023.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值