实验名称 实验四 多线程与并发
实验目的
1. 理解进程与线程的概念,掌握创建线程对象的方法。
2. 熟练使用线程类相关API以控制线程对象的状态。
3. 掌握实现线程同步的方法。
实验内容
使用继承Thread类创建线程
public class ThreadExample1 extends Thread {
@Override
public void run() {
try {
int sleepTime = (int) (Math.random() * 1000);
Thread.sleep(sleepTime);
System.out.println("Thread " + this.getId() + " finished after " + sleepTime + "ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new ThreadExample1();
thread.start();
}
}
}
使用实现Runnable接口创建线程
public class RunnableExample2 implements Runnable {
@Override
public void run() {
try {
int sleepTime = (int) (Math.random() * 1000);
Thread.sleep(sleepTime);
System.out.println("Thread " + Thread.currentThread().getId() + " finished after " + sleepTime + "ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new RunnableExample2());
thread.start();
}
}
}
2. 复现例11.5:教师唤醒班长,班长唤醒汤姆。与教材中给的代码不同,分别写出教师、班长、汤姆三个线程类,并在Demo类中创建教师、班长、汤姆三个线程对象,演示这三个线程对象的并发:教师唤醒班长,班长唤醒汤姆。
public class TeacherThread extends Thread {
private TomThread tomThread;
public TeacherThread(TomThread tomThread) {
this.tomThread = tomThread;
}
@Override
public void run() {
System.out.println("Teacher is waking up the Class Leader.");
tomThread.interrupt();
}
}
public class ClassLeaderThread extends Thread {
private TomThread tomThread;
public ClassLeaderThread(TomThread tomThread) {
this.tomThread = tomThread;
}
@Override
public void run() {
try {
sleep(2000); // Class Leader sleeps for 2 seconds
System.out.println("Class Leader is waking up Tom.");
tomThread.interrupt();
} catch (InterruptedException e) {
System.out.println("Class Leader is woken up early.");
}
}
}
public class TomThread extends Thread {
@Override
public void run() {
try {
while (true) {
sleep(1000); // Tom sleeps for 1 second
System.out.println("Tom is still sleeping.");
}
} catch (InterruptedException e) {
System.out.println("Tom is woken up by someone.");
}
}
}
public class Demo {
public static void main(String[] args) {
TomThread tomThread = new TomThread();
ClassLeaderThread classLeaderThread = new ClassLeaderThread(tomThread);
TeacherThread teacherThread = new TeacherThread(tomThread);
tomThread.start();
classLeaderThread.start();
teacherThread.start();
}
}
3.
(1)复现案例实践12:模拟管程方式实现1个生产者消费者问题。
package ahpu.t3;
import java.util.LinkedList;
class Monitor {
private LinkedList buffer = new LinkedList<>();
private int capacity = 5;
public synchronized void produce() {
while (buffer.size() == capacity) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int item = (int) (Math.random() * 100);
buffer.add(item);
System.out.println("Produced: " + item);
notify();
}
public synchronized void consume() {
while (buffer.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int item = buffer.remove();
System.out.println("Consumed: " + item);
notify();
}
}
class Producer extends Thread {
private Monitor monitor;
public Producer(Monitor monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (true) {
monitor.produce();
}
}
}
class Consumer extends Thread {
private Monitor monitor;
public Consumer(Monitor monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (true) {
monitor.consume();
}
}
}
public class Demo {
public static void main(String[] args) {
Monitor monitor = new Monitor();
Producer producer = new Producer(monitor);
Consumer consumer = new Consumer(monitor);
producer.start();
consumer.start();
}
}
(2)扩展1:模拟管程方式实现n个缓冲区的生产者消费者问题。
package ahpu.t3.t2;
import java.util.LinkedList;
class Monitor {
private LinkedList buffer = new LinkedList<>();
private int capacity;
public Monitor(int capacity) {
this.capacity = capacity;
}
public synchronized void produce() {
while (buffer.size() == capacity) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int item = (int) (Math.random() * 100);
buffer.add(item);
System.out.println("Produced: " + item);
notify();
}
public synchronized void consume() {
while (buffer.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int item = buffer.remove();
System.out.println("Consumed: " + item);
notify();
}
}
class Producer extends Thread {
private Monitor monitor;
public Producer(Monitor monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (true) {
monitor.produce();
}
}
}
class Consumer extends Thread {
private Monitor monitor;
public Consumer(Monitor monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (true) {
monitor.consume();
}
}
}
public class Demo {
public static void main(String[] args) {
int numBuffers = 5; // Number of buffer spaces
Monitor monitor = new Monitor(numBuffers);
int numProducers = 3; // Number of producer threads
int numConsumers = 2; // Number of consumer threads
for (int i = 0; i < numProducers; i++) {
Producer producer = new Producer(monitor);
producer.start();
}
for (int i = 0; i < numConsumers; i++) {
Consumer consumer = new Consumer(monitor);
consumer.start();
}
}
}
(3)扩展2:模拟PV操作实现n个缓冲区的生产者消费者问题。
package ahpu.t3.t3;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
class Monitor {
private LinkedList buffer = new LinkedList<>();
private int capacity;
private Semaphore emptySlots;
private Semaphore filledSlots;
private Semaphore mutex;
public Monitor(int capacity) {
this.capacity = capacity;
this.emptySlots = new Semaphore(capacity);
this.filledSlots = new Semaphore(0);
this.mutex = new Semaphore(1);
}
public void produce() {
try {
emptySlots.acquire();
mutex.acquire();
int item = (int) (Math.random() * 100);
buffer.add(item);
System.out.println("Produced: " + item);
mutex.release();
filledSlots.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void consume() {
try {
filledSlots.acquire();
mutex.acquire();
int item = buffer.remove();
System.out.println("Consumed: " + item);
mutex.release();
emptySlots.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Producer extends Thread {
private Monitor monitor;
public Producer(Monitor monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (true) {
monitor.produce();
}
}
}
class Consumer extends Thread {
private Monitor monitor;
public Consumer(Monitor monitor) {
this.monitor = monitor;
}
@Override
public void run() {
while (true) {
monitor.consume();
}
}
}
public class Demo {
public static void main(String[] args) {
int numBuffers = 5; // Number of buffer spaces
Monitor monitor = new Monitor(numBuffers);
int numProducers = 3; // Number of producer threads
int numConsumers = 2; // Number of consumer threads
for (int i = 0; i < numProducers; i++) {
Producer producer = new Producer(monitor);
producer.start();
}
for (int i = 0; i < numConsumers; i++) {
Consumer consumer = new Consumer(monitor);
consumer.start();
}
}
}
实验程序及结果(附录)
思考
关键字synchronized有哪几种用法?各自的逻辑是什么?
关键字 `synchronized` 在 Java 中有三种主要用法,各自的逻辑如下:
1. **实例方法同步**:
```java
public synchronized void myMethod() {
// 方法体
}
```
这种用法将整个实例方法标记为同步方法。当一个线程调用这个方法时,它将获得实例对象的锁,其他线程将被阻塞,直到当前线程执行完该方法并释放锁。
2. **同步块**:
```java
public void myMethod() {
synchronized (lockObject) {
// 同步块的代码
}
}
```
这种用法允许你创建一个同步块,只有当线程获得了指定对象(`lockObject`)的锁时才能执行同步块中的代码。这允许更细粒度的控制,不必锁住整个方法,而只锁住需要同步的代码块。
3. **静态方法同步**:
```java
public static synchronized void myStaticMethod() {
// 方法体
}
```
静态方法同步使用 `synchronized` 关键字来修饰静态方法。它将锁定整个类的所有实例,而不是特定实例对象。这意味着只有一个线程能够同时执行任何一个静态同步方法。
`synchronized` 关键字的作用是确保多个线程之间的协调和互斥访问共享资源。当一个线程进入同步块或方法时,它会尝试获取对象的锁,如果锁已被其他线程占用,线程将被阻塞,直到锁被释放。这确保了共享资源的安全访问,防止多个线程同时修改共享数据,从而避免数据竞争和不一致的结果。