多线程学习(九)

原子类

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锁)

通过同步控制块,而不是对整个方法的进行同步,可以使多个任务访问对象的时间性能得到显著的提高。
下面是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);
	}
}

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

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

比较 synchonized 整个方法和 synchonized 代码块的对比:

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

posted @ 2017-05-08 14:44  风中小蘑菇  阅读(110)  评论(0编辑  收藏  举报