读书笔记:多线程程序设计23个要点
1.多线程中有主内存和工作内存。在JVM中,有一个主存,负责所有线程共享数据;每个线程都有自己私有的工作内存,主存和工作内存分贝在JVM的堆栈区和堆区。
2.线程状态为“就绪”、“运行”、“睡眠”、“阻塞”和“等待”。
“就绪”表示线程正在等待CPU分配允许的运行时间。
3.线程的运行顺序不是我们创建它们的顺序,CPU处理线程的顺序也是不确定的。如果需要确定,您必须手动干预并使用setPriority()方法来设置优先级。
4.我们无法知道线程何时运行。当两个或多个线程访问同一个资源时,它们需要同步
5.每个线程都会注册自己,实际上在某个地方有一个对它的引用。因此,垃圾收集机制对此“束手无策”。
6.守护线程和一般线程的区别在于,一旦主程序结束,守护线程也会结束。
7.一个对象中的所有同步方法共享一个锁,这可以防止多个方法同时写入通用内存。同步的静态方法可以在一个类的范围内相互锁定。
8.访问关键共享资源的所有方法都必须设置为synchronized,否则它将无法正常工作。
9.假设已知一个方法不会引起冲突,最明智的办法就是不要使用synchronized,这样可以提高一些性能。
10.如果一个“同步”方法修改了一个变量,而我们的方法需要使用这个变量(可能是只读的),那么将我们自己的方法设置为synchronized。
11.同步不能被继承。父类的方法是同步的,所以“同步”不会在它的子类重载方法中被继承。
12.线程阻塞有几个原因:
(1)线程正在等待一些IO操作
(2)线程试图调用另一个对象的“synchronize”方法,但该对象被锁定,暂时不可用。
13.原子操作,对原始变量的操作是原子的。意味着这些操作是线程安全的,但在大多数情况下,我们无法正确使用它们。我们来看看i = i+1,I是int,属于本原变量:
(1)从主存储器读取I值到本地存储器。
(2)将值从本地内存加载到线程工作副本中。
(3)加载变量1。
(4)把I加到1上。
(5)将结果交给变量I。
注意,原子操作仅限于从步骤1到步骤2的读取和从步骤6到步骤7的写入,I的值仍可能被同时执行i=i+1的多线程中断所扰乱(在步骤4中)。
doublelong变量是长原子变量。该数组是非原子类型的对象。
14.根据第13条,我们的解决方案是:
类XXX扩展线程{
/I将被频繁修改
private int I;
公共同步int read(){ return I;}
public synchronized void update(){ I = I+1;}
..........
}
15.volatile变量,也就是说必须和主存一致,实际上是“变量同步”,也就是说volatile变量的操作是原子性的,比如之前的long或者double变量。
16.使用yield()会自动放弃CPU,有时候比睡眠更能提升性能。
17.sleep()和wait()的区别在于,wait()方法在被调用时会被解锁,但我们可以使用它的地方只是在同步的方法或代码块内。
18.通过制造缩小同步范围,尽可能实现代码块同步。wait(毫秒)可以在指定的毫秒数内退出等待;对于wait(),需要通过notisfy()或notifyAll()来启动。
19.构建两个线程间的实时通信有几个步骤:
(1)。创建PipedWriter和PipedReader以及它们之间的管道;
piped reader in = New piped reader(New piped writer())
(2)。在需要发送信息的线程启动之前,将外部PipedWriter指向其内部Writer实例out
(3)。在需要接收信息的线程启动之前,在
(4)中将外部PipedReader引导到其内部Reader实例。这样,所有放入out的东西都可以从In中提取出来。
20.除了性能下降,同步带来的问题还有死锁死锁的缺点。只有精心设计才能防止死锁,除此之外别无他法。这也是线程难以驯服的一个原因。停止使用stop() suspend() resume()和destory()方法。
21.当大量线程被阻塞时,优先线程首先运行。但不代表低级线程不会运行,运行概率小。
22.线程组的主要优点是一个命令就可以完成整个线程组的操作。很少使用线程组。
23.从以下几个方面提高多线程的性能:
检查所有可能的阻塞,并尽可能使用sleep或yield()和wait();
尽可能延长睡眠时间(毫秒);
运行的线程不超过100个,不要太多;
不同平台linux或windows和不同JVM的运行性能差别很大。
0条评论