在介绍阻塞和非阻塞之前,我们先来了解一下多线程间一个重要的概念——临界区。
临界区——一种公有的资源或者共享数据,它可以被多个线程使用。临界区资源一次只能被一个线程使用,其它线程必须等待上一个线程
执行完成之后,才能使用。临界区资源是多线程之间重要保护对象,当临界区资源同时被多个线程访问时,容易出现错误。
代码示例:
/** * @author php * @date 2018/7/2 */ public class CriticalRegion { //定义1000元 public static Integer money = 1000; public static void main(String[] args) throws InterruptedException { //定义公共资源 CriticalRegion criticalRegion = new CriticalRegion(); //定义10个线程来购物 ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { executorService.execute(() -> { criticalRegion.shop(); }); } Thread.sleep(1000); System.out.println("最终还有:" + CriticalRegion.money); executorService.shutdown(); } //我们每次购物使用100块钱 public void shop() { money = money - 100; System.out.println("使用了100元,我还有:" + CriticalRegion.money); } }
执行结果:从中可以看出,多个线程,同时操作临界区资源时,容易导致操作资源错误。在大部分情况下,这是一个很严重的问题。
大家可以多执行几次,看看结果有什么不同。
现在,我们修改一下shop()方法,在方法上加上sychronized。执行结果:sychronized可以让临界区资源,一次只能被一个线程访问,
从而结果正确。
临界区资源,在使用多线程时,要格外的注意,以免结果千奇百怪。
在我们了解临界区之后,再来了解阻塞和非阻塞的概念。
阻塞和非阻塞通常被用来形容多线程间的相互影响。当一个线程占用了临界区资源,那么其它需要使用这个资源的线程都必须在这个临界区上等待。等待会导致线程挂起,这样就形成了阻塞。如果占用资源的线程一直没有释放资源,那么其它的线程在这个临界区上都不能继续工作。
相反,非阻塞表明多个线程之间的执行是不会相互影响的。
通常,我们使用synchronized关键字,ReentrantLock(重入锁)时,我们得到的线程就是阻塞线程。阻塞线程在执行代码前,都会尝试得到临界区资源的锁,如果得不到,线程就会一直挂起,直到临界区资源释放。