java并发编程笔记

来源:互联网 发布:淘宝网会员注册流程 编辑:程序博客网 时间:2024/05/21 23:31
-------------------任务执行-------------------1.CountDownLatch(闭锁) countDown():计数器,当一个线程调用就减1  await() 只有countDown计算机为零以后,await调用的进程才能继续执行。完成指定数量的线程以后,被await的线程才能继续执行(好比旧社会家里没有吃的,小孩吃完粗粮以后,父母才把剩下的吃了)小孩就是指定的多少个线程,父母就是被await方法调用的对象//例子CountDownLatch指定的是10个线程//contdl.await();只有指定的10个线程都执行完成以后,执行这个方法的线程才能继续public static void testCountDownLatch(final CountDownLatch contdl){for(int i=0;i<10;i++){System.out.println("start"+i);Thread t=new Thread(new Runnable(){@Overridepublic void run() {try{System.out.println("thread start!");}catch(Exception e){}finally{contdl.countDown();}}});t.start();System.out.println("end"+i);}}2.FutureTask 例子:public static void testFutureTask() throws InterruptedException, ExecutionException{FutureTask<String> fuTask=new FutureTask<String>(new Callable<String>(){@Overridepublic String call() throws Exception {System.out.println("call run");return "true";}});Thread thread=new Thread(fuTask);thread.start();String flag=fuTask.get();System.out.println(flag);}执行完成后,会输出ture的字符串,如果在调用get方法之前还没完成线程的执行,可能会中断4.semaphore信号量 用来控制并发过程中,可以执行的进程数量。(同一时间内只能有固定数量的可以执行,好比厕所的坑数量一样)//Semaphore  semaphore=new Semaphore(2);public static void testSemaphore(final Semaphore semaphore){for(int i=0;i<10;i++){Thread t=new Thread(new Runnable(){@Overridepublic void run() {try{semaphore.acquire();System.out.println("thread start!");Thread.currentThread().sleep(5000);}catch(Exception e){}finally{semaphore.release();}}});t.start();}}同时定义了10个线程,但是能同时执行的只有2个。因为在传入的参数进指定了,只能有2个执行5.CyclicBarrier栅栏 用来阻塞一些方法,只有满足CyclicBarrier的条件,才可以放行CyclicBarrier cyclicBarrier=new CyclicBarrier(10,new Runnable(){@Overridepublic void run() {System.out.println("runnable");}});Test.testCyclicBarrier(cyclicBarrier);public static void testCyclicBarrier(final CyclicBarrier cyclicbarrier){for(int i=0;i<10;i++){Thread t=new Thread(new Runnable(){@Overridepublic void run() {try{System.out.println("thread start1!");Thread.currentThread().sleep(2000);cyclicbarrier.await();System.out.println("thread start2!");}catch(Exception e){e.printStackTrace();}}});t.start();}}代码会输出thread start1!以后进入阻塞,只有当满足10个的时候,才会执行CyclicBarrier中的runnable线程,完了以后才会执行thread start2!6.在正常使用中我们多用list(arrayList)和map(hashMap),在并发中为了保证线程安全用ConcurrentHashMap和CopyOnWriteList7.Executor接口,实现Executor接口的接口ExecutorService和Executor的区别就是添加了生命周期的方法8.线程池的使用Executors.newFixedThreadPool(num);创建一个固定大小的线程池Executors.SimpleThreadPool();单线程Executors.newScheduledThreadPool();支持定时任务的线程池Executors.newCachedThreadPool();缓存线程池,可以创建任意个线程池,用完自动回收example:public static void testExecutor() throws InterruptedException, ExecutionException{ExecutorService executor=Executors.newSingleThreadExecutor();Callable<String> task=new Callable<String>(){@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName());System.out.println("running!");return "ok";}};Future<String> future=executor.submit(task);System.out.println(future.get());executor.shutdown();System.out.println(executor.isShutdown());}//outpool-1-thread-1running!oktrue//现实的获取代码段try {System.out.println(future.get(1,TimeUnit.SECONDS));//指定多少秒内获取到数据,如果在指定的时间内没有获取到数据会TimeoutException} catch (TimeoutException e) {//e.printStackTrace();future.cancel(true);//如果抛出TimeoutException异常,就取消该任务。}编写多线程高性能的代码是如果任务执行的时间较长,可以考虑使用限时的等待方法,在指定的时间内如果没有执行完成,就直接取消。合理的执行线程池的大小对于性能的提高也很有影响包和策略:(当队列被填满以后,包和策略就可以派上用场了)AbortPolicy:默认使用该测试,如果队列满了,会抛出RegectdExecutionExecption,我们可以捕获异常做一些处理CallerRybsPolicy:调用者运行,当使用该策略以后,会将调用线程的执行回退给调用者(可能是主线程)去执行,只要主线程就不会很快速的提交到队列,如果大并发厉害的话,这样也可能导致tcp队列和客户端整体性能下降(因为提交的越来越多),不过这样可以给线程池足够的时间去消费队列中的任务。DiscardPloicy:抛弃任务,增加任务DiscardOldestPolicy:会将队列中下一个任务抛弃掉,尝试将新任务提交到队列中去,如果是一个优先级高的队列会有问题的~9.毒丸对象(一个标志)用来控制线程(队列)的结束例如在生产者消费者队列中,如果要停止该执行线程,只需要产生一个毒丸对象,放到到队列中,当消费者检测到存在毒丸对象的时候退出执行。10.如果递归循环没有数据联系,在必要的情况下也可以通过并行来提交执行性能。11.Lock和ReentrantLock,ReadWriteLock-ReenrantReadWriteLock(Lock的实现类)必要的时候可以使用lock.tryLock();用来获取锁12.如果线程在休眠或者阻塞时持有一个锁,通常是一种不好的做法,影响其他线程对资源的访问,可能造成更大的性能下降13.线程的6种状态:new runnable blocked waiting timewaiting terminated14.线程中常用的方法:join():当线程A创建并调用了线程B,线程B调用了join方法的时候,线程A会挂起,直到线程B执行完成以后才会继续执行。(串联)join(long min)setDaemon(true):线程A调用A.setDaemon(true)就是将线程A设置为守护线程setUncaughtExceptionHandler(new ExceptionDemo()):用来捕获非运行时异常例子:public class ExceptionDemo implements UncaughtExceptionHandler{@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(t.getName());System.out.println(e.getMessage());}public static void main(String[] args) {Thread d=new Thread(new Current());d.setUncaughtExceptionHandler(new ExceptionDemo());d.start();}}class Current implements Runnable{@Overridepublic void run() {System.out.println(Integer.parseInt("BANC"));}}15ThreadLocal使用:属性线程私有化public class ThreadLocalDemo implements Runnable{private Date date2=new Date();private  ThreadLocal<Date> date=new ThreadLocal<Date>(){@Overrideprotected Date initialValue() {return new Date();}};@Overridepublic void run() {System.out.println("date2"+date2);System.out.println(date.get());}public static void main(String[] args) {ThreadLocalDemo td=new ThreadLocalDemo();for(int i=0;i<4;i++){new Thread(td).start();try {Thread.currentThread().sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}输出:date2Thu Mar 19 15:44:49 CST 2015Thu Mar 19 15:44:49 CST 2015date2Thu Mar 19 15:44:49 CST 2015Thu Mar 19 15:44:51 CST 2015date2Thu Mar 19 15:44:49 CST 2015Thu Mar 19 15:44:53 CST 2015date2Thu Mar 19 15:44:49 CST 2015Thu Mar 19 15:44:55 CST 2015//输出很明显-------------性能和伸缩性---------------------并发导致的性能影响因素:1.上下文切换应用程序,操作系统,JVM都需要使用同一个CPU,就需要协调数据,导致性能开销2.内存同步  (影响较小现在JVM会对代码进行优化,对于单线程执行的代码会去掉同步锁,对于集中处理可能方法粒度加快操作等)3.阻塞(存在锁竞争,导致操作系统上下文切换)减少锁的竞争:(减少锁持有的时间,降低锁的请求频率,使用带有协调机制的独占锁)1.缩小锁的范围(快进快出)对于那些和同步无关的代码,尽量放到同步代码块的外面,尤其一些i/o操作,计算逻辑大的2.减小锁的粒度(锁分解) 假如我们有2个集合,都进行add操作,我们完全可以定义两把锁,各自add使用各自的锁(保证原子性的前提下)。3.锁分段:就是定义一组锁,根据一定的逻辑计算,每个线程访问对应的锁exmaple:private final Object[] locks=new Object[16];private final int n=16;synchronized(lock[i%n]){...} 4.替代独占锁使用并发容器,读写锁,不可变对象和原子变量等5.不要使用对象池(虽然可以对对象进行重复使用,减少GC回收性能,但是在并发环境中,可能因为操作同一个对象导致阻塞)

0 0
原创粉丝点击