用于进行测试的程序表现为两个线程对一个Count类分别增加50000000次,并返回运行时间
用于进行数字操作的类
class Count{
private /*volatile*/ int count = 0;
private AtomicInteger atomicCount = new AtomicInteger(0);
private ReentrantLock lock = new ReentrantLock();
private WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
public /*synchronized*/ void increse(){
synchronized(this){
this.count++;
}
}
public int getCount(){
return this.count;
}
}
用于对数字操作类进行增加操作的增加类
class Increment implements Runnable{
Count counter = null;
public Increment(Count counter){
this.counter = counter;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0; i < 50000000; i++){
counter.increse();
}
}
}
用于观察结果的主类
public class Observer {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ExecutorService executor = Executors.newFixedThreadPool(5);
Count counter = new Count();
long before = System.currentTimeMillis();
executor.submit(new Increment(counter));
executor.submit(new Increment(counter));
while(((ThreadPoolExecutor)executor).getActiveCount() > 0){
Thread.yield();
}
executor.shutdown();
long after = System.currentTimeMillis();
System.out.println(counter.getCount() + " " + (after - before));
}
}
未进行线程同步的结果:
由于未进行同步导致运算的结果有误。
1、Synchronized关键字
synchronized是Java的原生同步器,通过直接加在方法上用于修饰或作为代码块的形式来进行同步。当用于修饰方法时以操作该方法的对象或类作为锁进行同步,当作为代码块时以传入的Object作为锁进行同步。
1、直接用于修饰方法的结果
public synchronized void increse(){
this.count++;
}
此时由调用该方法所属的对象object作为锁,即认为锁是“this”。
此时的运算结果为(取三次运行结果以尽量避免误差):
运算结果正确。
2、以this作为锁使用同步代码块
public /*synchronized*/ void increse(){
synchronized(this){
this.count++;
}
}
此时将调用该方法所属线程对象的地址作为锁进行同步。
此时的运算结果为(取三次运行结果以尽量避免误差):
运算结果正确,但是所消耗的时间与直接修饰方法相比较并没有明显差异。
2、ReentrantLock
ReentrantLock是java.util.concurrent包下的用于进行线程同步的类,其主要作用与synchronized关键字相差不大,但是其提供更多样的进程同步方式,如可被打断的请求锁的操作、限时的请求锁操作和设定条件的同步锁。
private ReentrantLock lock = new ReentrantLock();
public /*synchronized*/ void increse(){
lock.lock();
this.count++;
lock.unlock();
}
此时是通过利用ReentrantLock来进行行间的同步操作,在一个线程通过lock()请求到同步锁到该线程通过操作lock.unlock()这段时间内,其他需要进行this.count++操作的线程需要阻塞在lock.lock()处等待正在持有同步锁的线程执行lock.unlock()操作对同步锁进行释放。
此时的运算结果为(取三次运行结果以尽量避免误差):
运算结果正确,且通过输出得到的时间损耗可以看出同步锁相对于无论是通过代码块还是方法级的synchronized修饰都表现出更显著的性能优势。
3、AtomicInteger
AtomicInteger是java.util.concurrent.atomic包下的类,该类被称为原子整型类,该类将一般的数字操作都在计算机底层封装为原子性的操作,该类中大量用到了sun.misc.Unsafe类来绕过JVM对操作系统直接进行操作。当所需要进行同步的操作仅仅是一些简单的运算操作时,尽量使用原子类来进行原子性的运算操作。
class Count{
private AtomicInteger atomicCount = new AtomicInteger(0);
public /*synchronized*/ void increse(){
this.atomicCount.addAndGet(1);
}
public int getCount(){
return this.atomicCount.get();
}
}
可以看到通过利用原子数据类,方法中尽可能地避免了对重量级同步器的使用,使得性能得到显著的提高。
此时的运算结果为(取三次运行结果以尽量避免误差):
运算结果正确,且可以看到相对于使用重量级同步器,原子数据类的时间损耗极其可观