<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>懂了吗？ on 庞玉栋个人博客</title><link>https://pangyd.com/tags/%E6%87%82%E4%BA%86%E5%90%97/</link><description>Recent content in 懂了吗？ on 庞玉栋个人博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Thu, 02 Nov 2017 23:21:38 +0800</lastBuildDate><atom:link href="https://pangyd.com/tags/%E6%87%82%E4%BA%86%E5%90%97/index.xml" rel="self" type="application/rss+xml"/><item><title>java多线程高级教程，这些你都懂了吗？</title><link>https://pangyd.com/post/153-java/</link><pubDate>Thu, 02 Nov 2017 23:21:04 +0800</pubDate><guid>https://pangyd.com/post/153-java/</guid><description>&lt;p>一、countdownLatch和cyclicbarrier（这两个做多线程控制很好用，工作中会经常用到）&lt;/p>&lt;p>countdownLatch：主线程阻塞，当多个线程countdown到0，主线程执行；&lt;/p>&lt;p>cyclicbarrier：多个线程等待，当都处于等待状态了一起执行（类似于赛跑机制）&lt;/p>&lt;p>&lt;img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='120'%3E%3Crect width='480' height='120' fill='%230E1012' stroke='%231B1E22'/%3E%3Ctext x='240' y='66' fill='%235C6069' font-family='monospace' font-size='13' text-anchor='middle'%3E%5B%E5%8E%9F%E5%9B%BE%E5%B7%B2%E5%A4%B1%E6%95%88%5D%3C/text%3E%3C/svg%3E" img_width="641" img_height="517" alt="java多线程高级教程，这些你都懂了吗？" inline="0">&lt;/p>&lt;p class="pgc-img-caption" style="text-align: center">countdownLatch&lt;/p>&lt;p>&lt;img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='120'%3E%3Crect width='480' height='120' fill='%230E1012' stroke='%231B1E22'/%3E%3Ctext x='240' y='66' fill='%235C6069' font-family='monospace' font-size='13' text-anchor='middle'%3E%5B%E5%8E%9F%E5%9B%BE%E5%B7%B2%E5%A4%B1%E6%95%88%5D%3C/text%3E%3C/svg%3E" img_width="677" img_height="385" alt="java多线程高级教程，这些你都懂了吗？" inline="0">&lt;/p>&lt;p class="pgc-img-caption" style="text-align: center">cyclicbarrier&lt;br>&lt;/p>&lt;p>二、volatile和threadlocal&lt;/p>&lt;p>这两个也要很好掌握，不仅常用，而且面试时候也经常问到！&lt;/p>&lt;p>volatile:使属性可见性。有个属性是共享属性，这个关键字会在每个线程内开一块内存，每次子线程都会从主线程拿最新的属性放到内存中。但是有个问题，他只是拿最新的，比如：计数器，每个线程都执行+1操作。一个线程+1，另外两个同时读取这个属性，那么不会加2次，而是只加一次，这就是valatile不能保证原子性的原因。&lt;/p>&lt;p>threadlocal：用于做方法外的全局属性。这个跟volatile刚好相反，使全局变量独立，每个线程里都有一份独立副本。&lt;/p>&lt;p>&lt;img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='120'%3E%3Crect width='480' height='120' fill='%230E1012' stroke='%231B1E22'/%3E%3Ctext x='240' y='66' fill='%235C6069' font-family='monospace' font-size='13' text-anchor='middle'%3E%5B%E5%8E%9F%E5%9B%BE%E5%B7%B2%E5%A4%B1%E6%95%88%5D%3C/text%3E%3C/svg%3E" img_width="770" img_height="261" alt="java多线程高级教程，这些你都懂了吗？" inline="0">&lt;/p>&lt;p>这是hibernate的源码，如果当前线程没有session就openSession放到ThreadLocalMap中，每个线程的session都是独立的不会相互受影响。&lt;/p>&lt;p>三、AtomicInteger：支持原子性计数器。&lt;/p>&lt;p>之前说volatile不能作为计数器，那么多线程计数器要用atomic类的AtomicInteger&lt;/p>&lt;p>&lt;img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='120'%3E%3Crect width='480' height='120' fill='%230E1012' stroke='%231B1E22'/%3E%3Ctext x='240' y='66' fill='%235C6069' font-family='monospace' font-size='13' text-anchor='middle'%3E%5B%E5%8E%9F%E5%9B%BE%E5%B7%B2%E5%A4%B1%E6%95%88%5D%3C/text%3E%3C/svg%3E" img_width="977" img_height="576" alt="java多线程高级教程，这些你都懂了吗？" inline="0">&lt;/p>&lt;p>四、并发类容器&lt;br>&lt;/p>&lt;p>ConCurrentHashMap、CopyAndWriteArrayList、CopyAndWriteArraySet。&lt;/p>&lt;p>ConCurrentHashMap（底层分成16个hashtable来保证并发）读支持高并发读，写和删除时候只支持16个线程，所以一般也要上锁； copyandwrite支持高并发读，写的话要加锁（原理是，写的时候赋值一个容器，写好了把指针指向新容器并且删除原容器）&lt;/p>&lt;p>map、list、set都有并发类容器。面试时候经常会问hashmap和ConCurrentHashMap区别。这个demo我就不写了，用法和我们平时用的集合都一样，只是他们不会造成线程不安全。&lt;/p>&lt;p>五、并发类队列&lt;/p>&lt;p>阻塞的意思是，当达到队列最大容量或者为空，线程还往里加东西或取东西会被阻塞；有界意思，可以设置队列大小。&lt;/p>&lt;p>（非阻塞队列）ConcurrentLinkedQueue无阻塞无界限队列（高并发性能最好，peek取出不删除，poll取出删除），&lt;/p>&lt;p>（阻塞队列）ArrayBlockingQueue阻塞有界队列，LinkBlockingQueue阻塞无界队列（生产者消费者首选，take方法会阻塞，poll方法没有就返回null）。&lt;/p>&lt;p>这里我们以最经典的生产者消费者模式作为例子&lt;/p>&lt;p>&lt;img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='120'%3E%3Crect width='480' height='120' fill='%230E1012' stroke='%231B1E22'/%3E%3Ctext x='240' y='66' fill='%235C6069' font-family='monospace' font-size='13' text-anchor='middle'%3E%5B%E5%8E%9F%E5%9B%BE%E5%B7%B2%E5%A4%B1%E6%95%88%5D%3C/text%3E%3C/svg%3E" img_width="966" img_height="741" alt="java多线程高级教程，这些你都懂了吗？" inline="0">&lt;/p>&lt;p>这里我们顺便做一个延伸，我们用的所有线程池，包括面试时候经常问的要你自定义一个线程池怎么做？我们先看一下线程池的源码。&lt;/p>&lt;p>&lt;img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='480' height='120'%3E%3Crect width='480' height='120' fill='%230E1012' stroke='%231B1E22'/%3E%3Ctext x='240' y='66' fill='%235C6069' font-family='monospace' font-size='13' text-anchor='middle'%3E%5B%E5%8E%9F%E5%9B%BE%E5%B7%B2%E5%A4%B1%E6%95%88%5D%3C/text%3E%3C/svg%3E" img_width="812" img_height="153" alt="java多线程高级教程，这些你都懂了吗？" inline="0">&lt;/p>&lt;p>也就是说线程池也是用并发队列作为线程的容器。我们自定义时候也只需要重现实例化ThreadPoolExcutor，设置我们自己需要的参数即可。&lt;br>&lt;/p>&lt;p>六、分布式锁&lt;br>&lt;/p>&lt;p>这个其实已经不属于java代码范畴，但是我上一篇讲基础的时候，一些兄弟说我说的太简单，另一些又说太难，所以我加了一个分布式锁，希望能满足更多人需求。&lt;/p>&lt;p>分布式锁，主要用在分布式集群上，既然是分布式，那就有多个jvm，即使你代码写出花来也不能控制。所以只有一个办法就是借助第三方工具。比如memcache、redis、zk&lt;/p>&lt;p>1. 首先明确一点，有人可能会问是否可以考虑采用ReentrantLock来实现，但是实际上去实现的时候是有问题的，ReentrantLock的lock和unlock要求必须是在同一线程进行，而分布式应用中，lock和unlock是两次不相关的请求，因此肯定不是同一线程，因此导致无法使用ReentrantLock。&lt;/p>&lt;p>2. 基于数据库表做乐观锁，用于分布式锁。&lt;/p>&lt;p>3. 使用memcached的add()方法，用于分布式锁。&lt;/p></description></item></channel></rss>