• Java多线程笔记


    1、使用new Thread(runnableObj)方式,而非继承自Thread。


    对于耗时的任务。应放到线程中运行
    调用new Thread(runnable).start()方法启动线程,将会在线程中调用相应的runnalbe.run方法
    2、中断线程的方式:调用interrupt方式,会置位线程中断状态。检查这个中断位可推断线程是否被中断:Thread.currentThread().isInterrupted().
    currentThread可用于获取当前线程。
    sleep方法会清除中断状态并抛出InterruptedException。
    interrupt()会向线程发中断请求,并将中断状态置为true。


    interrupted()方法会检測是否线程被中断了,假设是会清除中断状态。


    线程状态:New,Runnable可执行(调用start后的状态。正在执行的线程状态以及等待执行的都是可执行),Blocked被堵塞,Waitting,Timed waiting计时等待,Terminated。




    被堵塞的情况:1、等待锁被释放;2、当线程等待还有一个线程通知调度器一个条件时,它自己进入等待状态;方法带有超时參数。超时时会进入计时等待状态。如Thread.sleep,Object.wait(),Thread.join(),Lock.tryLock(),Condition.await()


    3、join()等待终止指定的线程,join(long mills)等待指定线程死亡或经过指定的毫秒数;
    Thread.State getState()获取线程的状态
    4、线程继承自父线程的优先级。

    用setProperty调整优先级,取值0~10。值越大优先级越高。
    static void yield()此方法导致当前运行线程处于让步状态。有同优先级线程存在时,会优先运行。


    5、this.setDaemon(true)使线程转换为守护线程,此方法必须在线程启动前调用。守护线程的用途是为其它线程提供服务。当仅仅剩下守护线程时。虚拟机退出。

    守护线程应该永远不要去訪问固定资源。如文件、数据库,由于它在不论什么时候都可能在一个运行过程中发生中断。
    6、线程的run不能抛出被检測的异常。未被检測的异常会导致线程终止,线程死亡前异常会被传递到一个用于未捕获异常的处理器。该处理器实现了Thread.UncaughtExcetptionHandler接口。

    没安装默认处理器的话。默认处理器为空。不为独立的线程安装处理器。其处理器就是ThreadGroup对象
    不要在Catch语句中处理可被传递的异常。


    javap -c -v Bank 用于对Bank.class进行反编译


    7、採用synchronized保证代码块的并发訪问。


    ReentrantLock是一个锁对象,可用于控制并发。在一个公共訪问代码中声明一个锁对象。在try之前获得锁,finally里释放锁。而非将锁对象作为线程的成员。
    锁是可重入的,线程能够反复获取已持有的锁。锁保持一个持有计数,在临界区内调用其它方法时。计数加1。所调用的方法退出时,锁减1.
    留心临界区的异常处理。不要因异常而导致跳出临界区。
    8、条件对象用于线程等待某个条件满足时运行操作。

    单纯的锁无法满足这样的场景。
    一个锁能够关联多个条件变量Condition。bankLock.newCondition()创建锁关联的条件变量。

    在临界区内,while(condition no statisfy)condition.await();在循环体中调用是必须的。

    通过调用条件变量中的await方法会进入该条件的等待集,堵塞。等待还有一个线程调用同一条件上的signalAll方法(会解除全部等待这一条件的线程的堵塞,使得被解除堵塞的线程能够在当前线程退出堵塞之后,通过竞争实现对象的訪问)。signal()方法则是随机解除一个堵塞的线程。这个方式需保证被解除堵塞的线程能够运行,否则会造成死锁。
    每一个条件对象管理那些已经进入了被保护的代码段,但还不能执行的线程。




    每一个对象内都有一把锁。要调用synchronized声明的对象成员方法,必须事先获得内部的对象锁。


    内部的对象锁仅仅有一个关联的条件对象。wait会将线程加入到等待集中。notify/notifyAll会解除等待线程的堵塞状态。仅仅能在synchronized内部使用wait和notify方法实现条件对象等待某个条件实现的功能。

    模式与await和signal相似。


    一般不要用synchronized和Condition。优先用synchronized。


    线程有本地缓存和寄存器。能够暂存内存中的值。假设使用锁来运行并发訪问共享变量,编译器被要求在必要的时候刷新本地缓存(更新共享变量在本地的值)来保持锁的效应。
    volatile为同步訪问提供了免锁机制。可是不保证原子性。

    假设共享变量除了赋值外。不完毕其它操作,能够将其声明为volatile。
    atomic对象能够原子方式对变量做加减。


    9、public static final ThreadLocal<SimpleDateFormat> dataFormat= 
    new ThreadLocal<SimpleDateFormat>(){
    protect SimpleDateFormat initialValue(){
    return new SimpleDateFormat("yyyy-MM-dd");
    }
    }


    10、锁超时: if(myLock.tryLock(100,TimeUnit.MILLISECONDS)){...} 超时时抛出InterruptedException。可用于打破死锁。
    myCondition.await(100,TimeUnit.MILLISECONDS)


    11、读线程多写线程少用ReentrantReadWriteLock。
    private rwLock =  new ReentrantReadWriteLock();
    rLock=rwLock.readLock();该所被全部读线程共用,会排斥写操作在读之前用rLock.lock();结束后用rLock.unlock();
    wLock=rwLock.writeLock();排斥其它的读操作和写操作n。在写之前用wLock.lock();结束后用wLock.unlock();


    12、堵塞队列BlockingQueue(这个有几种继承类:LinkedBlockingQueue, LinkedBlockingDeque, ArrayBlockingQueue)就是一个生产者消费者模型中的用于存放共享数据的队列。该队列有考虑生产者消费者间的并发操作,处理生产者消费者速度的负载平衡。用offer加入一个元素、poll移出并返回队头、peek返回队头元素(这三种操作可指定操作的超时时间)。
    PriorityBlockingQueue优先队列。


    13、线程安全集合:堵塞队列、ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet(这两个是线程安全的有序集,需事先Comparable接口), ConcurrentLinkedQueue. 这些集合的size()内部须要通过遍历来确定集合大小,这与一般的集合不同。
    这些集合返回的是弱一致性的迭代器,不一定能返回被改动后的值,但不会将同一个值返回两次。不会抛出ConcurrentModificationException。
    ConcurrentHashMap<E>(int initialCapacity, float loadFactor, int concurrentLevel);中參数是指定集合的初始容量,默觉得16.concurrentLevel是并发写者线程的预计数目,表明可供多达16个写进程同一时候写,多了会堵塞剩下的线程。


    ConcurrentHashMap.putIfAbsent(k,v)是在该键不存在时插入kv对,否则返回键k相应的值。




    写数组的拷贝:
    CopyOnWriteArrayList和CopyOnWriteArraySet。写线程对底层数据做改动。

    假设读线程数大于写线程数。则这类集合非常适合。读写可不用同步就可保持一致性。




    不论什么集合类通过同步包装器可变为线程安全的,通过给集合增加锁来实现的,这不推荐使用,由于还是须要在訪问的代码中增加synchronized(synchashMap){...},推荐使用ConcurrentXXX集合:
    List<E> syncArrayList = Collections.synchronizedList(new ArrayList<E>());
    HashMap<K,V> syncHashMap = Collections.synchronizedMap(new HashMap<K,V>());
    对于常常被改动的数组列表,採用synchronized(arrayList){}会比CopyOnWriteArrayList性能更好。




    14、回调Callable和Future。 Runnalbe是无返回值无參数的异步方法。Callable和Future有返回值。Callable<E>{ E call();}仅仅有一个方法返回值类型为E的call().
    Future保存了异步计算结果。其get(long timeout,TimeUnit)可指定超时时间。超时时中断。假设Future还在运行。则isDone()返回false。cancle()方法可取消该运算。


    FutureTask包装器,可将Callable转化为Future何Runnable。
    Callable<Integer> myCompution=..;
    FutureTask<Integer> task= new FutureTask<Integer>(myCompution);//构造一个既是Future又是Runnable的对象。
    Thread t= new Thread(task);
    t.start();
    ...
    Integer result=task.get();//对get调用会堵塞直到有可获得的结果或者超时为止。


    15、运行器Executor
    假设程序中有大量生命期非常短的线程,最好使用线程池。线程池中包括了很多准备执行的空暇线程。

    将Runnable交给线程池,就会有一个线程调用Runnable.run()。run退出时线程又回到线程池等待下一次执行。
    使用线程池可降低并发线程数。
    Executor中有很多静态方法可用于构建线程池:newCachedThreadPool空暇线程被保存60s;newFixedThreadPool固定线程数且空暇线程一直保存,假设任务数大于线程数,未得到运行的任务会放到队列中等待空暇线程可用;newSingleThreadPool仅仅有一个线程,用于顺序运行。这些方法返回实现了ExecutorService接口的ThreadPoolExecutor对象。


    可用ExecutorService接口中的Future<?> submit(Runnable,T result);Future<?>submit(Callable<T> Task)方法提交Runnable和Callable对象给ExecutorService。


    线程池用完后调用shutdown()关闭线程池。


    步骤:
    a、Executors.newFixedThreadPool() 
    b、submit提交Runnable或Callable对象;
    c、获取Future对象result,并调用result.get();
    d、关闭线程池shutdown()




    15、预定运行(定时任务)
    ScheduledExecutorService接口中有为预定运行或反复运行的任务设计的方法。
    ScheduledExecutorService ss=Executors.newScheduledThreadPool();
    schedule(Callable/Runnable,initDelayTime,TimeUnit);
    scheduleAtFixedRate(Callable/Runnable,initDelayTime,long period,TimeUnit);//每隔period运行一次


    16、控制任务组
    Executor可用于控制一组相关任务。
    invokeAny:多个任务运行,仅仅要有一个运行完毕就能够结束了。
    invokeAll:多个任务运行,全部任务都运行完毕才干够结束。

    使用方式例如以下:
    List<Callable<T>> tasks=...; 
    List<Future<T>> results=executor.invokeAll(tasks);
    for(Future<T>res:results){//顺序遍历在第一个任务耗时非常长的情况下会花非常多时间进行等待。推荐用ExecutorCompletionService按结果产生顺序进行保存。更有效
    processFuturer(result.get());
    }


    17、fork-join框架(大任务划分为小任务,并行运行后merge)
    RecursiveTask用于有计算结果返回。RecursiveAction用于无计算结果返回。二者的compute用于生成并调用子任务并合并结果。


    class Counter extends RecursiveTask<Integer>{//分治算法
    protect Integer compute(){
    if(to-from<THRESHOLD){直接运行(单任务)}
    else{
    int mid=(low+high)>>1;
    Counter first=new Counter(from,mid);//拆分子任务
    Counter second=new Counter(mid+1,high);
    invokeAll(first,second);//调度全部子任务
    return first.join()+second.join();//合并结果
    }
    }
    }
    main(){
    ForkJoinPool pool=new ForkJoinPool();
    pool.invoke(counter);
    System.out.println(counter.join());
    }


    18、同步器
    提供了实现线程间相互合作的类:
    CyclicBarrier:等待一定数目的线程都到达一个公共屏障,在运行一个处理屏障的动作。


    CyclicBarrier barrier=new CyclicBarrier(numThreads,barrierAction);//当numThreads个线程都到达屏障时运行barrierAction
    Runnable{
    public void run(){
    doSomething...
    barrier.await();//等待直到屏障打开,可设置超时时间,超时时抛异常,会导致其它等待await的线程都抛BrokenBarrierException异常。
    ...
    }
    }


    CountDownLatch:同意等待直到计数器为0。

    用于当一个或多个线程需等到指定数目事件发生时。这是一次性的。仅仅可用一次。
    Exchanger:同意两个线程在要交换对象准备好时交换对象。两个线程在同一个数据结构的两个不同实例上。一个向实例加入数据。还有一个用于清除数据
    Semaphore:同意线程等待直到被同意运行为止。用于限制訪问资源的线程数。


    信号量管理了很多permits,用于限制通过的线程数量。信号量仅维护一个计数。其它线程通过调用release()释放许可permits。


    SynchronousQueue:同意一个线程把对象交给还有一个线程。在没显式同步时,两个线程将一个对象从一个线程传到还有一个线程。

  • 相关阅读:
    牛客-小V和gcd树
    CF-Codeforces Round #639 (Div. 2)-D-Monopole Magnets
    牛客-牛牛染颜色
    CF-637
    提高办公效率——快捷键
    FusionComputer虚拟机克隆后出现网卡服务重启失败解决办法
    centos7.5mini中Firewalld端口不通解决办法及想法
    FusionConputer热迁移过程记录
    Java集合框架之Iterable接口
    初识Java集合框架
  • 原文地址:https://www.cnblogs.com/llguanli/p/8340676.html
Copyright © 2020-2023  润新知