看了这篇文章:http://www.ciaoshen.com/2016/10/28/tij4-21/ 有一些Java并发的内容,另外查了一些资料。

朴素的Thread

首先,Java中关于线程Thread最基本的事实是:

  • 除非通过Native方法将本地线程加入JVM,创建线程唯一的方法就是“创建一个Thread类的实例对象,然后调用它的start()方法。”

其次,关于Thread对象实例的构造,需要注意,start()方法依赖于run()方法:

  • 要么传递一个Runnable对象给构造器做参数。
  • 要么重写Thread自己的run()方法。

第一种方法是实现Runnable接口。注意,Runnable里面获取线程信息需要用 Thread.currentThread()

  1. package com.company;
  2.  
  3. class MyRunnable implements Runnable {
  4. public void run() {
  5. try {
  6. Thread.sleep((long)(Math.random() % 5 * 1000 + 1000));
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.printf("Here is thread %d\n", Thread.currentThread().getId());
  11. }
  12. }
  13.  
  14. public class Main {
  15.  
  16. public static void main(String[] args) throws InterruptedException {
  17.  
  18. System.out.println("Hello!");
  19. MyRunnable myRunnable = new MyRunnable();
  20. Thread myThread1 = new Thread(myRunnable);
  21. Thread myThread2 = new Thread(myRunnable);
  22. myThread1.start();
  23. myThread2.start();
  24.  
  25. // Your Codec object will be instantiated and called as such:
  26. //System.out.printf("ret:%d\n", ret);
  27.  
  28. System.out.println();
  29.  
  30. }
  31.  
  32. }

第二种方法是直接继承Thread,需要多继承的,要用上一种Runnable接口的方法。

  1. package com.company;
  2.  
  3. class MyThread extends Thread {
  4. public void run() {
  5. try {
  6. Thread.sleep((long)(Math.random() % 5 * 1000 + 1000));
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.printf("Here is thread %d\n", getId());
  11. }
  12. }
  13.  
  14. public class Main {
  15.  
  16. public static void main(String[] args) throws InterruptedException {
  17.  
  18. System.out.println("Hello!");
  19. MyThread myThread1 = new MyThread();
  20. MyThread myThread2 = new MyThread();
  21. myThread1.start();
  22. myThread2.start();
  23.  
  24. // Your Codec object will be instantiated and called as such:
  25. //System.out.printf("ret:%d\n", ret);
  26.  
  27. System.out.println();
  28.  
  29. }
  30.  
  31. }

Executor和线程池

朴素的Thread对象,对映单个线程。多个Thread对象,多个线程是可以共存的。但会互相竞争资源。Executor创建一个“线程池”的概念,对线程统一管理。

Java SE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。

实验代码如下(有一些好的注意点):

  1. package com.company;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.concurrent.*;
  6.  
  7. class ConcurrentSum {
  8. private int coreCpuNum;
  9. private ExecutorService executorService;
  10. private List<FutureTask<Long>> tasks = new ArrayList<FutureTask<Long>>();
  11.  
  12. public ConcurrentSum() {
  13. coreCpuNum = Runtime.getRuntime().availableProcessors();
  14. System.out.printf("There's %d cores\n", coreCpuNum);
  15. executorService = Executors.newFixedThreadPool(coreCpuNum);
  16. }
  17.  
  18. class SumCalculator implements Callable<Long> {
  19.  
  20. int nums[];
  21. int start;
  22. int end;
  23. public SumCalculator(final int nums[], int start, int end) {
  24. this.nums = nums;
  25. this.start = start;
  26. this.end = end;
  27. }
  28.  
  29. @Override
  30. public Long call() throws Exception {
  31. long sum = 0;
  32. for (int i=start; i<end; i++) {
  33. sum += nums[i];
  34. }
  35. return sum;
  36. }
  37. }
  38.  
  39. public long sum(int[] nums) {
  40. int start, end, increment;
  41. for (int i=0; i<coreCpuNum; i++) {
  42. // 注意这里分片的方法是非常棒的
  43. increment = nums.length / coreCpuNum + 1;
  44. start = i * increment;
  45. end = start + increment;
  46. if (end > nums.length) {
  47. end = nums.length;
  48. }
  49. SumCalculator sumCalculator = new SumCalculator(nums, start, end);
  50. // FutureTask的构造参数是一个实现了Callable的对象
  51. FutureTask<Long> task = new FutureTask<Long>(sumCalculator);
  52. tasks.add(task);
  53. if (!executorService.isShutdown()) {
  54. executorService.submit(task);
  55. }
  56.  
  57. }
  58. return reduce();
  59. }
  60.  
  61. private long reduce() {
  62. long sum = 0;
  63. for (int i=0; i<tasks.size(); i++) {
  64. try {
  65. sum += tasks.get(i).get();
  66. } catch (InterruptedException e) {
  67. e.printStackTrace();
  68. } catch (ExecutionException e) {
  69. e.printStackTrace();
  70. }
  71. }
  72. // 如果没有下面这句,那么整个程序不会退出。
  73. executorService.shutdown();
  74. return sum;
  75. }
  76. }
  77.  
  78. public class Main {
  79.  
  80. public static void main(String[] args) throws InterruptedException {
  81.  
  82. System.out.println("Hello!");
  83.  
  84. // main routine
  85. int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  86. long sum = new ConcurrentSum().sum(arr);
  87. System.out.printf("sum: %d\n", sum);
  88.  
  89. // Your Codec object will be instantiated and called as such:
  90. //System.out.printf("ret:%d\n", ret);
  91.  
  92. System.out.println();
  93.  
  94. }
  95.  
  96. }

上面的reduce部分,使用了迭代循环获取各个FutureTask的结果,而如果某个结果还没有返回,则会阻塞。如果希望不阻塞,可以使用CompletionService。

  1. CompletionServiceExecutorService进行了包装,内部维护一个保存Future对象的BlockingQueue
  2.  
  3. 只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer
  4.  
  5. 它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。
  6.  
  7. 所以,先完成的必定先被取出。这样就减少了不必要的等待时间。

实验代码如下,和上面直接使用ExecutorService有一些区别:

  1. package com.company;
  2.  
  3. import java.util.concurrent.*;
  4.  
  5. class ConcurrentSum {
  6. private int coreCpuNum;
  7. private ExecutorService executorService;
  8. private CompletionService<Long> completionService;
  9.  
  10. public ConcurrentSum() {
  11. coreCpuNum = Runtime.getRuntime().availableProcessors();
  12. System.out.printf("There's %d cores\n", coreCpuNum);
  13. executorService = Executors.newFixedThreadPool(coreCpuNum);
  14. completionService = new ExecutorCompletionService<Long>(executorService);
  15. }
  16.  
  17. class SumCalculator implements Callable<Long> {
  18.  
  19. int nums[];
  20. int start;
  21. int end;
  22. public SumCalculator(final int nums[], int start, int end) {
  23. this.nums = nums;
  24. this.start = start;
  25. this.end = end;
  26. }
  27.  
  28. @Override
  29. public Long call() throws Exception {
  30. long sum = 0;
  31. for (int i=start; i<end; i++) {
  32. sum += nums[i];
  33. }
  34. return sum;
  35. }
  36. }
  37.  
  38. public long sum(int[] nums) {
  39. int start, end, increment;
  40. for (int i=0; i<coreCpuNum; i++) {
  41. // 注意这里分片的方法是非常棒的
  42. increment = nums.length / coreCpuNum + 1;
  43. start = i * increment;
  44. end = start + increment;
  45. if (end > nums.length) {
  46. end = nums.length;
  47. }
  48. SumCalculator sumCalculator = new SumCalculator(nums, start, end);
  49. // CompletionService直接提交一个实现了Callable的对象
  50. if (!executorService.isShutdown()) {
  51. completionService.submit(sumCalculator);
  52. }
  53.  
  54. }
  55. return reduce();
  56. }
  57.  
  58. private long reduce() {
  59. long sum = 0;
  60. for (int i=0; i<coreCpuNum; i++) {
  61. try {
  62. sum += completionService.take().get();
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. } catch (ExecutionException e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. // 如果没有下面这句,那么整个程序不会退出。
  70. executorService.shutdown();
  71. return sum;
  72. }
  73. }
  74.  
  75. public class Main {
  76.  
  77. public static void main(String[] args) throws InterruptedException {
  78.  
  79. System.out.println("Hello!");
  80.  
  81. // main routine
  82. int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  83. long sum = new ConcurrentSum().sum(arr);
  84. System.out.printf("sum: %d\n", sum);
  85.  
  86. // Your Codec object will be instantiated and called as such:
  87. //System.out.printf("ret:%d\n", ret);
  88.  
  89. System.out.println();
  90.  
  91. }
  92.  
  93. }

经过这样的改动,获取各个线程结果的地方就不会block了。

yield( )让步

和System.gc()方法类似,yield()方法仅仅是“建议”当前线程可以让给其他线程了。但完全不保证会让位。

未捕获异常

异常逃逸:主要原因是抛出异常的线程,和抓异常的代码所在的线程不是一个。这样即使在main函数里面抓异常也是抓不到的。

比如如下代码

  1. package com.company;
  2.  
  3. import java.util.concurrent.*;
  4.  
  5. class ConcurrentSum {
  6. // Runnable
  7. public class SuperException implements Runnable {
  8.  
  9. @Override
  10. public void run() {
  11. throw new RuntimeException();
  12. }
  13. }
  14. // Executor
  15. public void letsGo() {
  16. ExecutorService executorService = Executors.newCachedThreadPool();
  17. try {
  18. executorService.execute(new SuperException());
  19. } catch (Exception e) {
  20. System.out.println("Here catch Exception");
  21. e.printStackTrace();
  22. } finally {
  23. System.out.println("Here finally shutdown");
  24. executorService.shutdown();
  25. }
  26. }
  27. }
  28.  
  29. public class Main {
  30.  
  31. public static void main(String[] args) throws InterruptedException {
  32.  
  33. System.out.println("Hello!");
  34.  
  35. // main routine
  36. new ConcurrentSum().letsGo();
  37.  
  38. // Your Codec object will be instantiated and called as such:
  39. //System.out.printf("ret:%d\n", ret);
  40.  
  41. System.out.println();
  42.  
  43. }
  44.  
  45. }

就不会抓到线程,命令行输出:

  1. Hello!
  2. Exception in thread "pool-1-thread-1" java.lang.RuntimeException
  3. at com.company.ConcurrentSum$SuperException.run(Main.java:12)
  4. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  5. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  6. at java.lang.Thread.run(Thread.java:745)
  7. Here finally shutdown

如上,“异常逃逸”不是说异常就不见了,消失了。其实它还是会冒泡到控制台的。而且自作主张显示在异常报告的第一行。这里的”逃逸”是指异常逃脱了我们try{}catch{}语句对异常的处理。

  1. 逃逸的原因很容易猜,因为执行execute()方法的是主线程的Excecutor
    而抛出异常的线程池中被分配来执行run()的某线程。JVM的异常处理是各线程只管自己的事。
    所以同理,就算我们把异常处理套到main()方法的主体中也无法捕获异常。因为始终是在主线程里做动作,这是无法处理其他线程里的异常的。
  2.  
  3. 注意,C++里面也是这样。即使是在C++的join调用的外层,包上try-catch也没有用的,还是抓不到异常。

那有没有办法,在主线程里面捕获子线程的异常呢?有的!

重载ThreadFactory里面的newThread方法,在其中加上对UncauhgtExceptionHandler实现类的绑定。如下:

  1. package com.company;
  2.  
  3. import java.util.concurrent.*;
  4.  
  5. class ConcurrentSum {
  6. // Runnable
  7. class SuperException implements Runnable {
  8.  
  9. @Override
  10. public void run() {
  11. throw new RuntimeException();
  12. }
  13. }
  14.  
  15. class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
  16.  
  17. @Override
  18. public void uncaughtException(Thread t, Throwable e) {
  19. System.out.println("Caught exception: " + e);
  20. }
  21. }
  22.  
  23. class HandlerThreadFactory implements ThreadFactory {
  24.  
  25. @Override
  26. public Thread newThread(Runnable r) {
  27. System.out.println(this + " createing new Thread");
  28. Thread t = new Thread(r);
  29. System.out.println("created " + t);
  30. t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
  31. System.out.println("eh = " + t.getUncaughtExceptionHandler());
  32. return t;
  33. }
  34. }
  35.  
  36. // Executor
  37. public void letsGo() {
  38. ExecutorService executorService = Executors.newCachedThreadPool(new HandlerThreadFactory());
  39. try {
  40. executorService.execute(new SuperException());
  41. } catch (Exception e) {
  42. System.out.println("Here catch Exception");
  43. e.printStackTrace();
  44. } finally {
  45. System.out.println("Here finally shutdown");
  46. executorService.shutdown();
  47. }
  48. }
  49. }
  50.  
  51. public class Main {
  52.  
  53. public static void main(String[] args) throws InterruptedException {
  54.  
  55. System.out.println("Hello!");
  56.  
  57. // main routine
  58. new ConcurrentSum().letsGo();
  59.  
  60. // Your Codec object will be instantiated and called as such:
  61. //System.out.printf("ret:%d\n", ret);
  62.  
  63. System.out.println();
  64.  
  65. }
  66.  
  67. }

独占锁,synchronized关键字

可以加在方法上,如下:

  1. class Mutex implements Runnable{
  2. private volatile int num=0; //“private”禁止外部方法调用
  3. public synchronized void increment(){
  4. }
  5. }
  6.  
  7. 任何线程如果想要调用increment()方法,必须先获得当前Mutex类实例对象的唯一“独占令牌”,直到increment()方法执行完成,才释放令牌。
    在此期间,其他所有希望对同一个Mutex对象执行increment()操作的线程,都必须阻塞等候。

也可以加在代码临界区上:

  1. class Mutex implements Runnable{
  2. private volatile int num=0; //“private”禁止外部方法调用
  3. public void increment(){
  4. synchronizedthis){
  5. ...
  6. }
  7. }
  8. }
  9.  
  10. synchronized方法里面可以调用本身对象,也可以调用其他对象。

ReentrantLock,乐观锁

除了synchronized之外,另一个选择是使用ReentrantLock,又叫“乐观锁”。用法和效果和synchronized都差不多。差别是它必须显式地创建锁,锁住和解锁。

但ReentrantLock解决资源冲突的机制,和synchronized完全不同。它使用了非阻塞算法(non-blocking algorithms)。简单说就是:乐观地假设操作不会频繁地引起冲突,而是先进行操作,如果没有其他线程争用共享数据,那操作就成功了。如果共享数据被争用,产生了冲突,那就再进行其他的补偿措施(最常见的补偿措施就是不断地重试,直到试成功为止)。

如另一篇博文中所讲(link):synchronized的加锁机制也是有很多优化的,从偏向锁,到轻量级锁,到重量级锁,逐渐升级。其中,强量级锁比较类似ReentrantLock所采用的CAS机制;偏向锁甚至更加优化,只是在对象某个flag置一个偏向锁的标记以及持有这个偏向锁的ThreadId,然后只要不发生竞争,就没有问题;发生竞争了,就升级到轻量级锁。

  1. 非阻塞算法能奏效,基于一个前提条件:需要操作和冲突检测这两个步骤具备原子性,它靠硬件指令来保证,这里用的是 CAS 操作(Compare and Swap)。
    进一步研究 ReentrantLock 的源代码,会发现其中比较重要的获得锁的一个方法是 compareAndSetState,这里其实就是调用的 CPU 提供的特殊指令。
    直接用单个指令保证原子性。AutomicIntegerAutomicLongAutomicReference 等特殊的原子性变量类,它们提供的如:
    compareAndSet()、incrementAndSet()和getAndIncrement()等方法都使用了 CAS 操作。

看一下AtomicInteger源码是如何保证线程同步的:

  1. public final int getAndSet(int newValue) {
  2. for (;;) {
  3. int current = get();
  4. if (compareAndSet(current, newValue))
  5. return current;
  6. }
  7. }
  8. public final boolean compareAndSet (int expect, int update) {
  9. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  10. }

乐观锁因为没有频繁的上下文切换,效率较高。

关于volatile

  • Any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable.

这里的“happens-before relationship(偏序关系)”指的就是,必须保证如果值的改变发生在读取之前,那么这个改变要确确实实写进内存,让读取操作“可见”。

粗略说就是:每次值的写入都直接写进内存,而不使用CPU缓存的优化。

线程安全的三个关键词:“互斥性”“可见性”“原子性”

结束线程

ExecutorService#shutdown():不再接受新任务。

ExecutorService#shutdownNow():立刻终止所有任务。

ExecutorService#awaitTermination():阻塞直到所有现有任务完成,然后结束所有线程,关闭线程池。

线程(任务)的中断(interrupt)

Thread#interrupt()方法可以“试图”中断“阻塞中”的线程。注意只能中断处于”阻塞状态“的线程。但:

  • sleep阻塞是可以被中断的
  • IO阻塞是不可以被中断的
  • synchronized阻塞是不可以被中断的

如果任务不是由execute()执行,而是submit()执行,那在返回的Future上调用cancel(),可以有针对性地关闭线程池中的特定任务。

但要强制中断IO阻塞,可以直接关闭底层IO。另外和普通IO不同,nio是可以响应Future的cancel()中断的。

Synchronized的独占锁不可中断,但ReentrantLock的乐观锁是可以中断的。用Reentrant#lockInterruptibly()。因为上面分析过了,乐观锁本质上并没有阻塞冲突线程,它们只是在不断地重试而已。

中断任务的一个惯用法(良好实践)

由于Java的interrupt只能中断“处于阻塞状态中”的任务(虽然对于IO和synchronized锁造成的阻塞也无力中断),所以当线程处于“非阻塞状态”下愉快运行的时候,除非暴力结束线程,看起来我们没有办法中断某个任务。

一个可行的方法是用静态方法Thread.interrupted()判断当前线程是否收到interrupt命令。

  1. try{
  2. while(!interrupted()){
  3. //工作代码,一旦收到中断指令,就跳出
  4. }
  5. }catch(InterruptedException ie){
  6. //除非是在sleep()状态下被中断,否则不会捕获InterruptedException
  7. }finally{
  8. //非阻塞状态下被中断后的处理
  9. }

wait( ), notify( ), notifyAll( )

wait()阻塞挂起当前线程的同时,释放互斥锁。这点和sleep()不同,sleep()不释放互斥锁。

  1. someObject.notifyAll();
  2. someObject.wait();

先唤醒正在等待某个对象互斥锁的所有线程,然后再阻塞挂起当前线程,释放互斥锁,这样做是安全的。

另外wait()的一个惯用法是:尽量把wait()放在一个while(!condition){wait();}里面。防止醒来后却发现不满足条件的情况。

最后,对某个对象调用wait()和notify(),notifyAll()之前先获得这个对象上的互斥锁。

notify( )和notifyAll( )

notify()和notifyAll()的区别在于,notifyAll()唤醒所有排队线程,而notify()只唤醒其中一个线程,但却无法控制唤醒的是哪一个。

notifyAll()的策略就是,在这个锁上等的线程都叫醒。由线程自己判断这次的事务是否和自己有关。

notify()只叫醒一个线程,线程也需要自己判断这次的事务是否和自己有关。但notify()和notifyAll()的区别在于,如果任务和被唤醒的线程无关,继续睡之前,此线程还需要把接力棒传下去唤醒另一个线程,虽然它也不清楚唤醒的是哪个线程。

所以一般来说notifyAll()更合理一些。特殊情况用notify()要小心。

wait( )能被interrupt信号中断

这里有必要再次强调interrupt的有效范围:

  • 能中断sleep()阻塞
  • 能中断wait()阻塞
  • 无法中断synchronized互斥锁阻塞
  • 无法中断IO阻塞
  • 能中断ReentrantLock的乐观锁(笔者加)

尤其注意,当使用while(!Thread.interrupted())判断时,不要过早拦截InterruptedException导致无法跳出循环。

“生产者-消费者”模型

这是一个交叉模型,无论是生产者还是消费者,都秉持同一个逻辑:

  • 占在自己的锁上,条件不满足时一直等待。
  • 一旦条件满足,开始工作。必要时可以获取公共资源的锁。
  • 执行完任务,跑到对方的锁上唤醒对方的线程。

condition#await( ), condition#signalAll( )

除了wait()和notifyAll()来完成线程间的协作。conditon#await()和conditon#signalAll()也能实现同样的功能。

和wait()以及notifyAll()是附着于Object不同。conditon#await()和conditon#signalAll()是附着于Lock。

官方的例子:例子里通过两个条件来控制不同线程。

  • Condition notFull:”防满溢标签“。当数组存满100个元素时,防满溢标签放出await()方法“阻塞,挂起,释放锁”。只有同一个标签放出signalAll()才能终止await()让线程继续。
  • Condition notEmpty:”防空标签“。当数组中没有元素时,防空标签放出await()方法“阻塞,挂起,释放锁”。只有同一个标签放出signalAll()才能终止await()让线程继续。
  1. class BoundedBuffer {
  2. final Lock lock = new ReentrantLock();
  3. final Condition notFull = lock.newCondition();
  4. final Condition notEmpty = lock.newCondition();
  5.  
  6. final Object[] items = new Object[100];
  7. int putptr, takeptr, count;
  8.  
  9. public void put(Object x) throws InterruptedException {
  10. lock.lock();
  11. try {
  12. while (count == items.length)
  13. notFull.await();
  14. items[putptr] = x;
  15. if (++putptr == items.length) putptr = 0;
  16. ++count;
  17. notEmpty.signal();
  18. } finally {
  19. lock.unlock();
  20. }
  21. }
  22.  
  23. public Object take() throws InterruptedException {
  24. lock.lock();
  25. try {
  26. while (count == 0)
  27. notEmpty.await();
  28. Object x = items[takeptr];
  29. if (++takeptr == items.length) takeptr = 0;
  30. --count;
  31. notFull.signal();
  32. return x;
  33. } finally {
  34. lock.unlock();
  35. }
  36. }
  37. }

BlockingQueue

无论通过Object#wait(),notify()组合还是condition#await(),signal()组合,这种通过互斥锁握手来实现同步的策略还是有点复杂。

一个更简单的解决方案是BlockingQueue。它的特性主要有两点:

  1. 对它的操作是“线程安全”的。所以它内部肯定是维护着一个互斥锁的。操作和操作之间具有原子性。可以放心地用。
  2. 队列满了,插入操作会被阻塞挂起。空了,读取操作会被阻塞挂起。

然后通过各个例子来进一步加深理解和记忆吧,骚年:

(完)

Java并发学习 & Executor学习 & 异常逃逸 & 同步互斥Best Practice & wait/notify, conditon#await/signal的更多相关文章

  1. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  2. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  3. (转)java并发编程--Executor框架

    本文转自https://www.cnblogs.com/MOBIN/p/5436482.html java并发编程--Executor框架 只要用到线程,就可以使用executor.,在开发中如果需要 ...

  4. 【Java 并发】Executor框架机制与线程池配置使用

    [Java 并发]Executor框架机制与线程池配置使用 一,Executor框架Executor框架便是Java 5中引入的,其内部使用了线程池机制,在java.util.cocurrent 包下 ...

  5. Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程

    Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程 一.捕获异常并重新启用线程 public class Testun { public static voi ...

  6. Java并发编程深入学习

    上周的面试中,被问及了几个并发开发的问题,自己回答的都不是很系统和全面,可以说是"头皮发麻",哈哈.因此果断购入<Java并发编程的艺术>一书,该书内容主要是对ifev ...

  7. Java并发编程快速学习

    上周的面试中,被问及了几个关于Java并发编程的问题,自己回答的都不是很系统和全面,可以说是"头皮发麻",哈哈.因此果断购入<Java并发编程的艺术>一书,学习后的体会 ...

  8. 【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile

    章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译= ...

  9. 【java并发编程艺术学习】(二)第一章 java并发编程的挑战

    章节介绍 主要介绍并发编程时间中可能遇到的问题,以及如何解决. 主要问题 1.上下文切换问题 时间片是cpu分配给每个线程的时间,时间片非常短. cpu通过时间片分配算法来循环执行任务,当前任务执行一 ...

随机推荐

  1. HDU 4965 Fast Matrix Calculation 矩阵快速幂

    题意: 给出一个\(n \times k\)的矩阵\(A\)和一个\(k \times n\)的矩阵\(B\),其中\(4 \leq N \leq 1000, \, 2 \leq K \leq 6\) ...

  2. 手撸一套纯粹的CQRS实现

    关于CQRS,在实现上有很多差异,这是因为CQRS本身很简单,但是它犹如潘多拉魔盒的钥匙,有了它,读写分离.事件溯源.消息传递.最终一致性等都被引入了框架,从而导致CQRS背负了太多的混淆.本文旨在提 ...

  3. cache共享问题

    经测试发现,cache在web中与windows service中是不能共享的.但在windows service可以使用cache.

  4. python基础补漏-08-异常处理

    try: #正常代码逻辑 ins = raw_input("this is a tast:") print ins+1 except Exception,e: print e -- ...

  5. datatable 修改点击列头进行排序顺序

    一般点击排序时,是先升序后降序 可以通过如下代码修改排序规则 jQuery(function ($) { $(".datatable").dataTable({ "pag ...

  6. Django 中CSRF中间件 'django.middleware.csrf.CsrfViewMiddleware',

    1.Django中CSRF中间件的工作原理及form表单提交需要添加{% csrf_token %}防止出现403错误 CSRF # 表示django全局发送post请求均需要字符串验证功能:防止跨站 ...

  7. [python篇] [伯乐在线][1]永远别写for循环

    首先,让我们退一步看看在写一个for循环背后的直觉是什么: 1.遍历一个序列提取出一些信息 2.从当前的序列中生成另外的序列 3.写for循环已经是我的第二天性了,因为我是一个程序员 幸运的是,Pyt ...

  8. 使用Gson解析JSON数据

    本文使用gson对json进行解析处理 首先,下载gson包 ,本文使用(gson-1.6.jar) package com.whroid.java.json; import com.google.g ...

  9. Kafka介绍 (官方文档翻译)

    摘要:Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写.Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据. 这种动 ...

  10. kb-07线段树--10--dfs序建树

    /* hdu3974 dfs序建树,然后区间修改查询: */ #include<iostream> #include<cstdio> #include<cstring&g ...