Java线程与同步机制详解
立即解锁
发布时间: 2025-08-17 02:35:37 阅读量: 15 订阅数: 43 


Java编程基础与SCJP认证指南
### Java 线程与同步机制详解
#### 1. 线程创建
在 Java 中,创建线程有多种方式,其中实现 `Runnable` 接口相较于继承 `Thread` 类有一些优势。以下是具体原因:
- **继承限制**:继承 `Thread` 类后,子类无法再继承其他类;而实现 `Runnable` 接口的类则可以有更多的继承选择。
- **开销问题**:有些类可能仅仅需要具备可运行的特性,继承 `Thread` 类的全部开销可能过大。
下面通过两个示例展示不同的线程创建方式:
- **示例 13.1**:创建 `Thread` 对象并调用 `start()` 方法启动线程的代码位于客户端代码中,而非 `Counter` 类内部。
- **示例 13.2**:此功能被放在 `Counter` 类的构造函数中,而不是客户端代码里。
此外,内部类对于实现执行简单任务的线程很有用。以下匿名类代码可以创建并启动一个线程:
```java
(new Thread() {
public void run() {
for(;;) System.out.println("Stop the world!");
}
}).start();
```
#### 2. 线程创建相关复习问题
下面是一些关于线程创建的复习问题及答案:
|问题|选项|答案|
| ---- | ---- | ---- |
|13.1 以下哪种是启动新线程的正确方式?|(a) 仅创建一个新的 `Thread` 对象,线程会自动启动。<br>(b) 创建一个新的 `Thread` 对象并调用 `begin()` 方法。<br>(c) 创建一个新的 `Thread` 对象并调用 `start()` 方法。<br>(d) 创建一个新的 `Thread` 对象并调用 `run()` 方法。<br>(e) 创建一个新的 `Thread` 对象并调用 `resume()` 方法。|(c)|
|13.2 当继承 `Thread` 类来实现线程执行的代码时,应该重写哪个方法?|(a) `begin()`<br>(b) `start()`<br>(c) `run()`<br>(d) `resume()`<br>(e) `behavior()`|(c)|
|13.3 以下哪些陈述是正确的?|(a) `Thread` 类是抽象类。<br>(b) `Thread` 类实现了 `Runnable` 接口。<br>(c) `Runnable` 接口有一个名为 `start` 的单一方法。<br>(d) 对实现 `Runnable` 接口的对象调用 `run()` 方法将创建一个新线程。<br>(e) 当最后一个用户线程结束时,程序终止。|(b)(e)|
|13.4 尝试编译并运行以下程序会有什么结果?|```java<br>public class MyClass extends Thread {<br> public MyClass(String s) { msg = s; }<br> String msg;<br> public void run() {<br> System.out.println(msg);<br> }<br> public static void main(String[] args) {<br> new MyClass("Hello");<br> new MyClass("World");<br> }<br>}<br>```|(d) 程序将编译无误,运行时会打印 `Hello` 和 `World`,但顺序不可预测。|
|13.5 尝试编译并运行以下程序会有什么结果?|```java<br>class Extender extends Thread {<br> public Extender() { }<br> public Extender(Runnable runnable) {super(runnable);}<br> public void run() {System.out.print("|Extender|");}<br>}<br>public class Implementer implements Runnable {<br> public void run() {System.out.print("|Implementer|");}<br> public static void main(String[] args) {<br> new Extender(new Implementer()).start();<br> new Extender().start();<br> new Thread(new Implementer()).start();<br> }<br>}<br>```|(b) 程序将编译无误,每次运行时会以某种顺序打印两次 `|Extender|` 和一次 `|Implementer|`。|
|13.6 尝试编译并运行以下程序会有什么结果?|```java<br>class R1 implements Runnable {<br> public void run() {<br> System.out.print(Thread.currentThread().getName());<br> }<br>}<br>public class R2 implements Runnable {<br> public void run() {<br> new Thread(new R1(),"|R1a|").run();<br> new Thread(new R1(),"|R1b|").start();<br> System.out.print(Thread.currentThread().getName());<br> }<br> public static void main(String[] args) {<br> new Thread(new R2(),"|R2|").start();<br> }<br>}<br>```|(e) 程序将编译无误,每次运行时会以某种顺序打印一次 `|R1a|`、一次 `|R1b|` 和一次 `|R2|`。|
|13.7 尝试编译并运行以下程序会有什么结果?|```java<br>public class Threader extends Thread {<br> Threader(String name) {<br> super(name);<br> }<br> public void run() throws IllegalStateException {<br> System.out.println(Thread.currentThread().getName());<br> throw new IllegalStateException();<br> }<br> public static void main(String[] args) {<br> new Threader("|T1|").start();<br> }<br>}<br>```|(c) 程序将编译无误,每次运行时会打印 `|T1|` 并抛出 `IllegalStateException`。|
|13.8 尝试编译并运行以下程序会有什么结果?|```java<br>public class Worker extends Thread {<br> public void run() {<br> System.out.print("|work|");<br> }<br> public static void main(String[] args) {<br> Worker worker = new Worker();<br> worker.start();<br> worker.run();<br> worker.start();<br> }<br>}<br>```|(d) 程序将编译无误,每次运行时会打印两次 `|work|` 并抛出 `IllegalStateException`。|
#### 3. 线程同步
线程共享相同的内存空间,能够共享资源。但在某些关键情况下,希望同一时间只有一个线程可以访问共享资源。例如,多个用户同时对共享银行账户进行存款和取款操作,如果没有适当的规则,会危及账户数据的完整性。Java 提供了高级的同步概念来控制对共享资源的访问。
##### 3.1 锁机制
锁(也称为监视器)用于同步对共享资源的访问。锁可以与共享资源关联,线程需要先获取与资源关联的锁才能访问共享资源。在任何给定时间,最多只有一个线程可以持有锁,从而访问共享资源。锁实现了互斥(也称为 `mutex`)。
在 Java 中,所有对象(包括数组)都有一个锁。这意味着任何 Java 对象的锁都可以用于实现互斥。通过将共享资源与 Java 对象及其锁关联,该对象可以作为保护者,确保对资源的同步访问。同一时间只有一个线程可以访问由对象锁保护的共享资源。
对象锁机制强制执行以下同步规则:
- **获取锁**:线程在进入共享资源之前,必须先获取与该共享资源关联的对象锁。运行时系统会确保如果另一个线程已经持有该对象锁,其他线程无法进入共享资源。如果线程无法立即获取对象锁,它将被阻塞,即必须等待锁可用。
- **释放锁**:当线程退出共享资源时,运行时系统会确保对象锁也被释放。如果有其他线程正在等待该对象锁,它可以尝试获取锁以访问共享资源。
需要注意的是,程序不应假设线程获得锁所有权的顺序。
类也有特定于类的锁,类似于对象锁。这种锁实际上是对与类关联的 `java.lang.Class` 对象的锁。对于类 `A`,引用 `A.class` 表示这个唯一的 `Class` 对象。类锁的使用方式与对象锁类似,可用于实现互斥。
关键字 `synchronized` 和锁机制是实现代码同步执行的基础。代码的同步执行有两种方式:声明同步方法或同步代码块。
##### 3.2 同步方法
如果对象的方法应该一次只由一个线程执行,那么这些方法的声明应该使用 `synchronized` 关键字。希望执行同步方法的线程必须先获取对象的锁(即持有锁),才能进入对象执行该方法。这可以通过调用方法简单实现。如果锁已经被另一个线程持有,调用线程将等待。程序无需进行特殊操作。线程通过从同步方法返回来释放锁,允许下一个等待该锁的线程继续执行。
同步方法在方法可能以并发执行会破坏对象状态的方式操作对象状态的情况下非常有用。例如,栈的实现通常将 `push` 和 `pop` 两个操作定义为同步的,以确保元素的入栈和出栈操作是互斥的。如果多个线程共享一个栈,那么一个线程在另一个线程正在出栈时将无法入栈。这保证了在多个线程访问同一栈状态时栈的完整性。
以下是一个栈实现的示例:
```java
class StackImpl {
private Object[] stackArray;
private int topOfStack;
public StackImpl(int capacity) {
stackArray = new Object[capacity];
topOfStack = -1;
}
public boolean push(Object element) {
// (2a) 非同步
//public synchronized boolean push(Object element) {
// (2b) 同步
if (isFull()) return false;
++topOfStack;
```
0
0
复制全文
相关推荐










