zoukankan      html  css  js  c++  java
  • 多线程学习(九)

    原子类

    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 + "
    p2:" + p2 + "
    p3:" + p3 + "
    p4:" + 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 代码块的对比:

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

  • 相关阅读:
    如何提高完成端口的性能
    我回来了
    减少资源包中的图片,提高效率
    新的MOVE结构,和在项目中实际的感受
    截图小结
    本周小记
    css选择器
    CSS的三种引入方式
    A标签的四个伪类(L V H A)排序上的讲究
    关于CSS清理浮动的方法
  • 原文地址:https://www.cnblogs.com/joeCqupt/p/6825023.html
Copyright © 2011-2022 走看看