http://www.javashuo.com/article/p-uamljweq-dz.htmlhtml
Lock接口的最大优点是它为读和写提供两个单独的锁(ReentrantReadWriteLock),ReentrantReadWriteLock的特色是:“读读共享”,“读写互斥”,“写写互斥”。(附:Lock取款机示例)java
高性能缓存简易示例:ios
public class ReadWriteMap { private final Map<Object, Object> map; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); public ReadWriteMap(Map<Object, Object> map) { this.map = map; } public Object put(Object key, Object value) { try { writeLock.lock(); return map.put(key, value); } finally { writeLock.unlock(); } } public Object get(Object key) { try { readLock.lock(); return map.get(key); } finally { writeLock.unlock(); } } }
sleep() 是Thread类的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其余线程,可是对象的锁(或监视器)依然保持。休眠时间结束后线程自动回到就绪状态。编程
wait() 是Object类的方法,调用此方法会让当前线程暂停执行指定的时间,并放弃对象锁进入等待池(wait pool)。只有调用对象的notify()或notifyAll()方法才能唤醒等待池中的线程进入等锁池(lockpool)。若是线程从新得到对象的锁就能够进入就绪状态。缓存
wait()方法多用于线程间通讯,而sleep()只是在执行时暂停。安全
阻塞队列(BlockingQueue)是一个支持两个附加操做的队列。这两个附加的操做支持阻塞的插入和移除方法。
1)阻塞的插入:当队列满时,队列会阻塞插入元素的线程,直到队列不满。
2)阻塞的移除:当队列空时,队列会阻塞移除元素的线程,直到队列不空。多线程
public class BlockingQueue { private final List<Object> queue = new LinkedList<>(); private int capacity = 10; public BlockingQueue() { } public BlockingQueue(int capacity) { this.capacity = capacity; } public synchronized Object put(Object item) throws InterruptedException { while (queue.size() >= capacity) { wait(); } queue.add(item); notifyAll(); return item; } public synchronized void remove() throws InterruptedException { while (0 == queue.size()) { wait(); } queue.remove(0); notifyAll(); } public synchronized int getSize() { return queue.size(); } }
附:我在实现阻塞队列时遇到的一个问题 http://www.javashuo.com/article/p-qlcubcau-eb.html并发
http://www.javashuo.com/article/p-hcxhfmup-eg.html异步
http://www.javashuo.com/article/p-aohsrijj-dd.html性能
调用start()方法时,它会新建一个线程而后执行run()方法中的代码。
直接调用run()方法,则不会建立新线程,方法中的代码会在当前调用者的线程中执行。验证以下:
public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> System.out.println(Thread.currentThread().getName())); Thread thread2 = new Thread(() -> System.out.println(Thread.currentThread().getName())); thread1.setName("thread1"); thread1.start(); Thread.sleep(1000); thread2.setName("thread2"); thread2.run(); }
线程调度是指系统为线程分配处理器使用权的过程,主要调度方式分两种,分别是协同式线程调度和抢占式线程调度。
Java线程调度就是抢占式调度。
1)继承Thread类:看jdk源码能够发现,Thread类实际上是实现了Runnable接口的一个实例,继承Thread类后须要重写run方法并经过start方法启动线程。继承Thread类耦合性太强了,由于Java只能单继承,因此不利于扩展。
2)实现Runnable接口:经过实现Runnable接口并重写run方法,并把Runnable实例传给Thread对象,Thread的start方法调用run方法再经过调用Runnable实例的run方法启动线程。因此若是一个类继承了另一个父类,此时要实现多线程就不能经过继承Thread的类实现。
3)实现Callable接口:经过实现Callable接口并重写call方法,并把Callable实例传给FutureTask对象,再把FutureTask对象传给Thread对象。它与Thread、Runnable最大的不一样是Callable能返回一个异步处理的结果Future对象并能抛出异常,而其余两种不能。
Runnable接口中的run()方法的返回值是void,它只是纯粹地去执行run()方法中的代码而已。
Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合能够用来获取异步执行的结果。
1)发挥多核CPU的优点
若是是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。单核CPU上的"多线程"是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工做,多线程,能够真正发挥出多核CPU的优点来,达到充分利用CPU的目的。
2)防止阻塞
从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优点,反而会由于在单核CPU上运行多线程致使线程上下文的切换,而下降程序总体的效率。可是单核CPU咱们仍是要应用多线程,就是为了防止阻塞。试想,若是单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据,对端迟迟未返回又没有设置超时时间,那么整个程序在数据返回来以前就中止运行了。多线程能够防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
3)便于建模
这是另一个没有这么明显的优势了。假设有一个大的任务A,单线程编程,那么就要考虑不少,创建整个程序模型比较麻烦。可是若是把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别创建程序模型,并经过多线程分别运行这几个任务,那就简单不少了。
两个看上去有点像的类,都在java.util.concurrent下,均可以用来表示代码运行到某个点上,两者的区别在于:
1)CyclicBarrier的某个线程运行到某个点上以后,该线程即中止运行,直到全部的线程都到达了这个点,全部线程才从新运行;CountDownLatch则不是,某线程运行到某个点上以后,只是给某个数值-1而已,该线程继续运行。
2)CyclicBarrier只能唤起一个任务,CountDownLatch能够唤起多个任务。
3) CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了。
若是你的代码在多线程下执行和在单线程下执行永远都能得到同样的结果,那么你的代码就是线程安全的。
这个问题有值得一提的地方,就是线程安全也是有几个级别的:
1)不可变
像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新建立一个,所以这些不可变对象不须要任何同步手段就能够直接在多线程环境下使用。
2)绝对线程安全
无论运行时环境如何,调用者都不须要额外的同步措施。要作到这一点一般须要付出许多额外的代价,Java中标注本身是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet。
3)相对线程安全
相对线程安全也就是咱们一般意义上所说的线程安全,像Vector这种,add、remove方法都是原子操做,不会被打断,但也仅限于此,若是有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的状况下都会出现ConcurrentModificationException,也就是fail-fast机制。
4)线程非安全
这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类。
死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:
1)获取到线程的pid,能够经过使用jps命令,在Linux环境下还可使用ps -ef | grep java
2)打印线程堆栈,能够经过使用jstack pid命令,在Linux环境下还可使用kill -3 pid
另外提一点,Thread类提供了一个getStackTrace()方法也能够用于获取线程堆栈。这是一个实例方法,所以此方法是和具体线程实例绑定的,每次获取获取到的是具体某个线程当前运行的堆栈。
若是这个异常没有被捕获的话,这个线程就中止执行了。另外重要的一点是:若是这个线程持有某个对象的监视器,那么这个对象监视器会被当即释放。
经过在线程之间共享对象就能够了,而后经过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的。
1)经过平衡生产者的生产能力和消费者的消费能力来提高整个系统的运行效率,这是生产者消费者模型最重要的做用。
2)解耦,这是生产者消费者模型附带的做用,解耦意味着生产者和消费者之间的联系少,联系越少越能够独自发展而不须要收到相互的制约。