java并发编程知识梳理

2019-06-19  本文已影响0人  台风口的猪

@Slf4j
public class ThreadDemo {

    public static void main(String[] args) throws InterruptedException {
        // 启动10个线程
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        Counter counter = new Counter();
        // 同时500000个用户过来请求 
        for (int i = 1; i <= 500000; i++) {
           //            threadPool.submit(new Runnable() {
          //                @Override
         //                public void run() {
        //                    counter.increment();
       //                }
      //            }); 
            threadPool.submit(()->counter.increment());
        }
        /**
         * 将线程池状态置为SHUTDOWN,并不会立即停止:
         */
        threadPool.shutdown();

     /**
         * 当前线程阻塞,等所有已提交线程执行完 如果执行完则返回true 否则返回false
         */
      threadPool.awaitTermination(60, TimeUnit.SECONDS);

        System.out.println("counter  : " + counter.getCount());
    }
}


@Slf4j
public class Counter {

    int count = 0;

    public void increment() {
        count = count + 1;
    }

    public int getCount() {
        return count;
    }
}

结果

    counter.getCount()  结果为 499252  499758  499323 每次运行都产生不一样的结果

为什么不一样

    当线程调用increment()方法时,实际上执行了三个步骤
    1. 从内存得到count的值
    2.将count+1
    3.将操作后的值重新存储到内存中
    因为是三个步骤所以并不具备原子性

解决方法

使用 synchronized

@Slf4j
public class Counter {
    int count = 0;
     //  只需要加上synchronized即可
    public synchronized void increment() {
        count = count + 1;
    }

    public int getCount() {
        return count;
    }
}

使用 juc包中的原子类

@Slf4j
public class Counter {


    final AtomicInteger count = new AtomicInteger();
//    int count = 0;  //使用AtomicInteger代替int

    public void increment(){
        count.getAndIncrement();  
        //count++;  //使用 count.getAndIncrement(); 代替count++
    }

    public AtomicInteger getCount() {
        return count;
    }
}

使用ReentrantLock

@Slf4j
public class Counter {

    Lock lock =  new ReentrantLock();
    int count = 0;

    public void increment(){
        lock.lock();
        try{
            //业务逻辑处理
            count++;
        }finally {
            lock.unlock(); // 建议在finally处释放锁,以免在运行try{}块时报错,导致最终锁没有释放
        }
    }

    public Integer getCount() {
        return count;
    }
}

ExecutorService 的 shutdown() shutdownNow() awaitTermination() 详解

shutdown()

将线程池状态置为SHUTDOWN,并不会立即停止:
 1.停止接收外部submit的任务
 2.内部正在跑的任务和队列里等待的任务,会执行完
 3. 等到2完成后,才真正停止

shutdownNow()

将线程池状态置为STOP。企图立即停止,但是并不一定:
 1.将shutdown()一样,先停止接收外部提交的任务
 2.忽略队列里等待的任务
 3.尝试将正在跑的任务interrupt中断
 4.返回未执行的任务列表

awaitTermination(long timeOut, TimeUnit unit)

 当前线程阻塞
等所有已提交的任务执行完(包括正在跑的和队列中等待的)或者等超时时间到或者线程被中断,抛出InterruptedException
后返回true(shutdown请求后所有任务执行完毕)或false(已超时的任务)

shutdown()和shutdownNow()的区别

 shutdownNow()能立即停止线程池,停止所有的任务(包含正在执行的和正在等待的),风险较大
 shutdown()只是关闭提交通道,等所有的任务跑完后再停止

shutdown()和awaitTermination()的区别

shutdown()后不能再提交新的任务进去;
awaitTermination()后可以继续提交。
awaitTermination()是阻塞的返回结果是线程池是否已停止(true/false);
shutdown()不阻塞。

总结

优雅的关闭,用shutdown()
立马关闭,并得到未执行任务列表,用shutdownNow()
优雅的关闭,并允许关闭声明后新任务能提交,用awaitTermination()
上一篇下一篇

猜你喜欢

热点阅读