美文网首页
java并发编程 学习笔记

java并发编程 学习笔记

作者: nyle | 来源:发表于2016-07-24 16:55 被阅读63次

    记一下学习过程的觉得需要注意的点吧

    第二章、线程安全性

    无状态对象一定是线程安全的
    内置锁是可重入的,就是说获得锁的操作的粒度是一个『线程』,而不是一个『调用』。
    synchronized 函数,是对类的实例加锁。

    第三章、对象的共享

    在程序中,可能编译过程中会进行重排序。就是对代码中的执行顺序可能进行改变,这样的优势是充分利用流水线的作用。 (这个有点可怕呀)

    volatile
    关键字可以保证变量的可见性,因为对volatile
    的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache。volatile
    还有一个作用就是局部阻止重排序的发生

    对于非volatile类型的64位数值变量double和long
    jvm允许将64位的读操作和写操作分解成两个32位的操作,所以不考虑失效数据的问题,在多线程中共享
    可变的long和double也是不安全的,除非用volatile来声明他们。(这个也要注意下,可能webApi中,会修改一个全局变量,这个时候long的修改可能会读到一个错误的值)

    在构造函数中最好不要新建一个线程并启动,可以吧启动放在后续的函数中。因为可能在构造完成之前,线程就会访问到this的值。

    线程封闭

    • ad-hoc线程封闭
      这个好像说完全自己在代码里注意
    • 栈封闭
      相当于用局部变量
    • Threadlocal类
      不同线程调用的一个Threadlocal变量不一样
    第四章、对象的组合

    没啥,主要注意加锁的时候,要加在一个锁上面,不要加错了

    第五章、基础构建模块

    Hashtable和Vector是同步容器
    创建一个同步List Collections.synchronizedList(new ArrayList<Object>());

    通过并发容器来代替同步容器,可以极大的提高伸缩性并降低风险
    接下来介绍了一些并发容器:

    • cuncurrentHashMap 使用的是分段锁,实现了额外的原子操作,类似相等则移除之类的,保障他的迭代器不会影响。
    • copyOnWriteArrayList 每次修改的时候都会把底层数组复制一遍,这样保障他的迭代器不会影响。
    • LinkBlockList ArrayBlockList.是FIFO队列
    • PriorityBlockingQueue 是一个优先级的队列
    • 双端队列 Deque ,可以实现,生产者首先读取自己的队列,空了再去别人的队列尾部开始工作,降低了队列的竞争程度

    中断
    首先,忘掉Thread.stop方法。虽然它确实停止了一个正在运行的线程,然而,这种方法是不安全也是不受提倡的,这意味着,在未来的JAVA版本中,它将不复存在。
    中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。
    因此,如果线程Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。Listing C这个示例描述了该技术。
    http://www.cnblogs.com/simonshi/archive/2011/12/31/2308455.html 这个讲的还可以

    同步工具类

    • CountDownLatch是一种灵活的闭锁实现,await会一直阻塞到,值为0.countDown会把值减1(闭锁,门一开始是关闭的,等待所需要的资源都初始化之后,会打开,并且一直打开)
    • FutureTask,Callable。一种有结果的Runnable.future.get() 可以获得
    • 信号量Semaphore
    • CyclicBarrier 感觉和countDownLatch差不多,只是可以重复使用
    第六章、任务执行

    Executor 任务执行框架
    如果在程序中实现一个生产者消费者的设计,那么最简单的方式就是使用Executor

    延时任务和周期性任务
    Timer类和ScheduledThreadPoolExecutor。
    Timer有一些缺陷,Timer在执行任务时只会创建一个线程,如果某个任务执行时间过长,将会影响其他的TimerTask的精确性。而且如果TimerTask抛出了一个未检查的异常,那么Timer将变现出糟糕的行为。
    在java 5.0或更高的jdk中,将很少使用Timer。

    如果要构建自己的调度系统,可以使用DelayQueue。(可以设置过期时间,然后take的时候,只能取出到了时间的)

    可以用ExecutorService的submit(callable a)方法来得到一个Future,然后future.get()得到返回值。但是这里所有的都会在future.get()处等待
    可以使用ExecutorCompletionService来进行处理,在提交多个task之后,起一个轮训,取completion.take(),这样可以获取完成了的线程。

    为任务设置时限:
    1.可以使用future.get(long timeout, TimeUnit unit),然后catch住TimeOutException

    对于要对多个futureTask设定实现,可以使用invokeAll(List<Callable> **, long timeOut, TimeUtil unit)

    相关文章

      网友评论

          本文标题:java并发编程 学习笔记

          本文链接:https://www.haomeiwen.com/subject/xbxgjttx.html