2014.10.26
And it's actually critically important to get used to things because it's a useful adaptive mechanism to keep track on new events and objects.
习惯化实际上也是至关重要的,因为这是一种非常有用的适应机制,可以让你注意到新鲜事物。
You have to have the courage to examine who you really are, to come to terms with the dark corner of your own soul.
你必须有审视自己的勇气,面对并接受你灵魂中的黑暗角落。
They're a couple whose marital blessing had been granted by none other than God himself.
一对步入了婚姻殿堂的新人,得到了上帝的祝福。
并发编程使我们可以将程序划分为多个分离的、独立运行的任务。通过使用多线程机制,这些独立任务中的每一个都将由执行线程来驱动。
线程可以驱动任务,因此你需要一种描述任务的方式,这可以由Runnable接口来提供。要想定义任务,只需实现Runnable接口并编写run()方法。
将Runnable对象转变为工作任务的传统方式是把它提交给一个Thread构造器。
Java SE5中的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。
在任何线程池中,现有线程在可能的情况下,都会被自动复用。
Runnable是执行工作的独立任务,但是它不返回任何值。如果你希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口。
影响任务行为的一种简单方法是调用sleep(),这将使任务中止执行给定的时间。
线程的优先级将该线程的重要性传递给了调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的先执行。优先级较低的线程仅仅是执行的频率较低,而不是得不到执行。
你可以用getPriority()来读取现有线程的优先级,并且在任何时刻都可以通过setPriority()来修改它。
yield()只是一个暗示,没有任何机制保证它将会被采纳。
后台线程是指在程序运行时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中所有后台线程。
必须在线程启动之前调用setDarmon()方法,才能把它设置为后台线程。
Thread类自身不执行任何操作,它只是驱动赋予它的任务。
一个线程可以在其它线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。
一个任务不能依赖另一个任务,因为任务关闭的顺序无法得到保证。
如果失败的概率非常低,那么即使存在缺陷,它们也可能看起来是正确的。
你永远都不知道一个线程何时在运行。
第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它,以此类推。
java以提供关键字synchronized的形式,为防止资源冲突提供了内置支持。当任务要执行被synchronized关键字保护的代码片段的时候,它将检查锁是否可用,然后获取锁,执行代码,释放锁。
在使用并发时,将域设置成private是非常重要的,否则,synchronized关键字就不能防止其他任务直接访问域,这样就会产生冲突。
同步规则:如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且读写线程都必须用相同的监视器锁同步。
一个任务可以多次获得对象的锁。
每个访问临界共享资源的方法都必须被同步,否则它们就不会正确的工作。
还可以使用显式的Lock对象。-----内建的synchronized锁
2014.10.28
A life, a fulfilling life, a rich life includes ups and downs, includes pain and getting up again, includes failure and getting up again.
生活,令人满意的生活,丰富的生活包括了起起落落,包括了痛苦和再次振作,包括了失败和再次奋斗。
The one real objective of education is to leave a man in the condition of continually asking questions.
教育的真正目的,是让一个人可以连续发问。
Mike actually holds the world record for downhill speed skiing for blind person, which is 60 miles an hour.
实际上,迈克是盲人高山滑雪的记录保持者,速度达到了每小时60英里。
原子操作是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。
原子性可以应用于除long和double之外的所有基本类型之上的“简单操作”。
如果你将一个域声明为volatile的,那么只要对这个域产生了写操作,那么所有的读操作就都可以看到这个修改。即便使用了本地缓存,情况也确实如此,volatile域会立即被写入到主存中,而读取操作就发生在主存中。
如果多个任务在同时访问某个域,那么这个域就应该是volatile的,否则,这个域就应该只能经由同步来访问。
同步也会导致向主存中刷新,因此如果一个域完全由synchronized方法或语句块来防护,那就不必将其设置为是volatile的。
一个任务所作的任何写入操作对这个任务来说都是可视的,因此如果它只需要在这个任务内部可视,那么你就不需要将其设置为volatile的。
当一个域的值依赖于它之前的值时,volatile就无法工作了。如果某个域的值受到其他域的限制,那么volatile也无法工作。
使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。你的第一选择应该是使用synchronized关键字,这是最安全的方式,而尝试其他任何方式都是有风险的。
Java SE5 引入了诸如AtomicInteger,AtomicLong,AtomicReference等特殊的原子性变量类。
临界区:防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法,它也使用synchronized关键字建立。通过使用同步控制块,而不是对整个方法进行同步控制,可以使多个任务访问对象的时间性能得到显著提高。
宁愿使用同步控制块而不是对整个方法进行同步控制的典型原因:使得其他线程能更多地访问(在安全的情况下尽可能多)
防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储ThreadLocal对象通常当作静态域存储。在创建ThreadLocal时,你只能通过get()和set()方法来访问该对象的内容,其中,get()返回与其线程相关联的对象的副本,而set()会将参数插入到为其线程存储的对象中,并返回存储中原有的对象。ThreadLocal保证不会出现竞争条件。
一个线程可以处于以下四种状态之一:
新建(new):当线程被创建时,它只会短暂地处于这种状态。此时它已经分配了必须的系统资源,并执行了初始化。此刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态。
就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,它就可以运行;这不同于死亡和阻塞状态。
阻塞(Blocked):线程能够运行,但有某个条件阻止它的运行。当线程处于阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间。直到线程重新进入了就绪状态,它才有可能执行操作。
死亡(Dead):处于死亡或终止状态的线程将不再是可调度的,并且再也不会得到CPU时间,它的任务已结束,或不再是可运行的。任务死亡的通常方式是run()方法返回,但是任务的线程还可以被中断。
当你使用线程来同时运行多个任务时,可以通过使用锁(互斥)来同步两个任务的行为,从而使得一个任务不会干涉另一个任务的资源。
线程之间的协作:
任务之间的握手。在互斥之上,为任务添加了一种途径,可以将其自身挂起,直至某些外部条件发生变化,表示是时候让这个任务向前开动了为止。
wait()使你可以等待某个条件发生变化,而改变这个条件超出了当前方法的控制能力。通常这种条件将由另一个任务来改变。wait()会在等待外部世界产生变化的时候将任务挂起,并且只有在notify()或notifyAll()发生时,即表示发生了某些感兴趣的事物,这个任务才会被唤醒并去检查所产生的变化。因此,wait()提供了一种在任务之间对活动同步的方式。
调用sleep()的时候锁并没有被释放,调用yield()也属于这种情况。
当一个任务在方法里遇到了对wait()的调用的时候,线程的执行被挂起,对象上的锁被释放。这意味着另一个任务可以获得这个锁。因此在该对象中的其他synchronized方法可以再wait()期间被调用。这一点至关重要,因为这些其他的方法通常将会发生改变,而这种改变正是使被挂起的任务重新唤醒所感兴趣的变化。
wait()、notify()、notifyAll()这些方法是基类Object的一部分,而不属于Thread的一部分。
实际上,只能在同步控制方法或同步控制块里调用wait()、notify()和notifyAll()(因为不用操作锁,所以sleep()可以在非同步控制方法里调用)
显式的Lock和Condition对象,只有在更加困难的多线程问题中才是必须的。
在许多情况下,你可以瞄向更高的抽象级别,使用同步队列来解决任务协作问题,同步队列在任何时刻都只允许一个任务插入或移除元素。
通过输入/输出在线程间进行通信通常很有用。提供线程功能的类库以“管道”的形式对线程间的输入/输出提供了支持。PipedReader与普通I/O之间最重要的差异——PipedReader是可中断的。
任务之间相互等待的连续循环,没有哪个线程能继续。这被称之为死锁。
当以下四个条件同时满足时,就会发生死锁:
1.互斥条件
2.至少有一个任务它必须持有一个资源且正在等待获取一个当前被别的任务持有的资源。
3.资源不能被任务抢占,任务必须把资源释放当作普通事件。
4.必须有循环等待
要防止死锁的话,只需破坏其中一个即可。