- 线程的同步
1. 线程同步是对不同线程的协调。
2. 线程之间的四种关系(1)不相关的线程。(2)相关但无需同步的线程。(3)互斥线程。(4)相互通信式互斥线程。
3. 不相关的线程。用以执行不同功能的线程之间没有任何交互关系。
4. 相关但无需同步的线程。可用于分解处理任务。采用一组线程,使之分别作用于同一数据结构的不同数据部分,线程之间没有交互关系。eg1. 为每个套接字连接请求创建一个新的线程。eg2. 守护线程。
5. 守护线程。demon或daemon。守护线程以后台方式运行,用于为其他程序或线程提供服务。
// 设定为守护线程 myThread.setDemon(true);
Java程序不会停止守护线程。eg. 维护打印机队列的线程。
6. 启动多个相同的线程,为解决同一问题而分别处理不同的数据部分。
7. 测试一个给定的数值是否为素数。把给定的数值分成不同的数值段,从而确定线程的数量,由每个线程分别测算划定数值段内的每一个数值。
质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。
class TestRange extends Thread{ static long possPrime; long from, to; TestRange(int from, long possPrime){ this.possPrime = possPrime; this.from = (from == 0) ? 2 : from; this.to = from + 99; } public void run(){ for(long i = from; i <= to && i < possPrime; i++){ if(possPrime % i == 0){ break; } yield(); } } }
8. 在Java API中,Thread类中有三个方法已被停用:(1)挂起suspend(); (2)恢复resume(); (3)停止stop();
9. 线程的停止。应当采用编码方式的实现,即通过修改某一变量,通知目标线程停止运行。目标线程应该定期查看这个变量。
10. 如果想要停止因I/O问题而挂起或处于封锁状态的线程,可通过调用interrupt()方法,向线程发送一个异常信息。
11. 互斥线程。在一定的时间内,线程需要采用互斥方式运行。当线程需要操作同一数据结构中的同一数据部分时,这些线程必须相互等待以免同时修改同一数据,造成数据结果的不确定性。这些线程就是互斥线程。
12. join()方法。主线程将使用线程的join()方法等待每个线程的完成。
public class P { // 气压表的当前读数 static int pressureGauge = 0; // 气压安全范围上线 static final int safetyLimit = 20; public static void main(String[] args) { Pressure[] p1 = new Pressure[10]; for(int i = 0; i < 10; i++) { p1[i] = new Pressure(); p1[i].start(); } // 等待线程结束 for(int i = 0; i < 10; i++) { try { p1[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("气压表当前读数是:" + pressureGauge + "\n气压安全范围上线:" + safetyLimit); } static class Pressure extends Thread { void raisePressure() { if(P.pressureGauge < P.safetyLimit - 15) { try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } P.pressureGauge += 15; } } public void run() { raisePressure(); } } }
13. 当两个或多个线程同时更新同一个数值时,竞争条件(数据竞争)就会出现。在通常的数据竞争情况下,最困难的问题是无法调试。这是不可重复的问题,我们无法预知数据何时会出现不一致的情况。
14. 线程同步的解决方案。用以确保并发情况下数据的完整性。
(1)测试和设置指令。所有的通用CPU都具有这种指令。指令本身不提供封锁功能,但可以基于这种指令实现高级的同步控制机制。
(2)PV信号灯。信号灯一直是一种主要的同步原语。信号灯可以进行加减计数:P意味着减1操作,V意味着加1操作。利用测试和设置指令很容易实现信号灯控制原理。信号灯是一种底层的控制机制,程序员很难读取和调试,但可以使用信号灯实现高级的控制机制。
(3)读写锁(互斥锁)。提供一种简单的同步控制机制。在任何给定的时刻,只有一个线程能够执行互斥锁保护的代码块。利用信号灯,很容易实现互斥锁。
(4)监控器。是一种基于锁和变量的高级同步控制机制,其中的变量可用于跟踪某些相关的条件。利用读写锁,很容易实现监控器。监控器定义了若干方法,其中两个预定义的方法是wait()和notify()。
15. 在采用何种同步技术方面,并没有一个通用的标准。
16. 避免数据竞争。(1)无论两个线程何时访问同一个数据,必须采用互斥的方法进行处理。(2)在任意时刻允许两个线程同时读数据,但不允许有两个以上的线程对同一数据同时进行读写。(3)两个线程更不能同时写一个数据。
17. 线程互斥。互斥是一种保持同步的协议,可以确保当一个线程访问某一特定的数据时,其他线程不会再操作同一数据。在Java中,线程的互斥是建立在对象数据基础之上的。系统中的任何对象都可以用做线程的同步控制。我们可以显式地使用synchronized关键字,或隐式地提供一个用于同步的对象。运行时系统将负责控制并提供必要的代码,确保在一个给定的时刻最多只有一个线程能够锁住特定的对象。
18. synchronized(同步的)关键字。可用于(1)类方法(2)实例方法(3)代码块。在每一种情况下,都必须获取一个指定对象的互斥锁,然后才能执行代码块,直到代码块执行结束后才释放锁。
19. 程序员惟一需要做的是:指定需要互斥执行的代码区和对象。在指定互斥执行的代码区时要尽可能地小一些,因为互斥执行会影响系统的性能。
20. 使用synchronized关键字。同步代码块。如果可能的话,最好使用正被更新的对象作为同步锁。
在这个例子中,被更新的变量是整数类型,都不是对象类型,所以借用Object对象。只有获取对象o的互斥锁,才可以执行指定的代码区。
public class P { // 气压表的当前读数 static int pressureGauge = 0; // 气压安全范围上线 static final int safetyLimit = 20; static Object o = new Object(); public static void main(String[] args) { Pressure[] p1 = new Pressure[10]; for(int i = 0; i < 10; i++) { p1[i] = new Pressure(); p1[i].start(); } // 等待线程结束 for(int i = 0; i < 10; i++) { try { p1[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("气压表当前读数是:" + pressureGauge + "\n气压安全范围上线:" + safetyLimit); } static class Pressure extends Thread { void raisePressure() { if(P.pressureGauge < P.safetyLimit - 15) { try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } P.pressureGauge += 15; } } public void run() { synchronized (o) { raisePressure(); } } } }
21. 同步类方法。在这种情况下,提供互斥锁的对象是隐式的。
把一个static修饰的方法指定为同步的(synchronized),在任何给定的时间里,只有一个类对象可以执行static synchronized修饰的方法。
static class Pressure extends Thread { static synchronized void raisePressure() { if(P.pressureGauge < P.safetyLimit - 15) { try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } P.pressureGauge += 15; } } public void run() { raisePressure(); } }
22. 同步实例方法。在这种情况下,提供互斥锁的对象是隐式的,也就是方法调用对应的this对象。
为了把一个实例的方法定义为互斥的,可以把关键字synchronized加到实例方法的前面。