多线程编程是指在一个程序中同时执行多个线程,每个线程都是独立运行的、拥有自己的执行路径和指令序列。在Java中,多线程编程可以使用java.lang.Thread类或者实现java.lang.Runnable接口来创建线程。
多线程编程可以提高程序的并发性和效率,特别适用于需要同时执行多个任务或者处理多个事件的场景。
目录
1. 线程创建和启动:
使用Thread类的构造方法创建线程对象,然后调用start()方法来启动线程。线程启动后会自动执行run()方法中的代码。
1). 使用Thread类创建和启动线程:
- 第一步,创建一个Thread子类,并重写其run()方法,该方法中包含线程需要执行的代码
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
System.out.println("Hello from MyThread!");
}
}
- 第二步,实例化Thread的子类,并调用start()方法来启动线程。
public class Main {
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}
在上述代码中,我们创建了一个名为`MyThread`的Thread子类,并重写了它的`run()`方法,在`run()`方法中打印了一条消息。然后在`Main`类的`main()`方法中,我们实例化了`MyThread`类,并调用`start()`方法来启动线程。
线程的启动通过`start()`方法来实现,该方法会启动一个新的线程并调用该线程的`run()`方法。注意,不要直接调用`run()`方法,这样不会创建新的线程,而是在当前线程中执行`run()`方法。
2). 实现Runnable接口创建和启动线程:
- 第一步,创建一个实现了Runnable接口的类,并实现其run()方法。
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的代码
System.out.println("Hello from MyRunnable!");
}
}
- 第二步,实例化实现了Runnable接口的类,并将其传递给Thread类的构造方法中。
public class Main {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
在上述代码中,我们创建了一个名为`MyRunnable`的实现了Runnable接口的类,并实现了其`run()`方法,在`run()`方法中打印了一条消息。然后在`Main`类的`main()`方法中,我们实例化了`MyRunnable`类,并将其传递给`Thread`类的构造方法中,最后调用`start()`方法启动线程。
通过实现Runnable接口来创建线程的好处是可以避免Java单继承的限制,可以继续继承其他类。此外,使用Runnable接口还可以使代码更具可读性和可维护性。
无论是使用Thread类还是实现Runnable接口,线程的创建和启动都是通过实例化线程对象并调用start()方法来实现的。启动线程后,线程会在自己的执行路径上执行run()方法中的代码。
2. 线程同步和互斥:
线程同步和互斥是多线程编程中重要的概念,用于确保多个线程对共享资源的安全访问。下面我将详细介绍线程同步和互斥的概念,并提供具体的代码解释。
线程同步是指协调多个线程之间的执行顺序,以保证它们对共享资源的访问是安全的。当多个线程同时访问共享资源时,可能会出现线程之间的数据竞争和冲突,导致程序运行出错或产生不确定的结果。为了避免这种情况,我们可以使用同步机制来保证线程的互斥访问,即同一时间只有一个线程可以访问共享资源。
在Java中,常用的线程同步机制包括使用synchronized关键字和Lock接口实现的锁机制。下面分别介绍这两种方式的线程同步:
1). 使用synchronized关键字: - 对象级别的同步:使用synchronized关键字修饰方法或代码块,保证在同一时间只有一个线程能够访问被synchronized修饰的代码块或方法。
public class MyThread {
private int count;
public synchronized void increment() {
count++;
}
}
在上述示例中,我们使用synchronized关键字修饰了`increment()`方法,保证了对`count`变量的原子性操作。同一时间只有一个线程能够执行`increment()`方法。 - 对象内部锁:使用synchronized关键字修饰一个对象,实现对该对象的内部锁定。
public class MyThread implements Runnable {
private Object lock;
public MyThread(Object lock) {
this.lock = lock;
}
public void run() {
synchronized (lock) {
// 互斥代码块
}
}
}
在上述示例中,我们使用synchronized关键字将互斥代码块 `synchronized (lock)` 置于需要同步的位置。这里的`lock`对象可以是任意对象,它在多个线程之间充当同步锁。
2). 使用Lock接口实现的锁机制: Lock接口提供了更为灵活的锁定机制,相比synchronized关键字,它提供了更多的功能和对细粒度的控制。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread {
private int count;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
在上述示例中,我们使用了Lock接口及其实现类ReentrantLock来实现线程同步。通过调用`lock()`方法获取锁,并在finally块中调用`unlock()`方法释放锁。
使用Lock接口的好处是可以更加灵活地控制锁的获取和释放,例如使用try-finally语句块确保锁在异常情况下能够正确释放。
无论是使用synchronized关键字还是Lock接口,线程同步都能够确保在同一时间只有一个线程能够访问共享资源,从而避免数据竞争和冲突的发生。
3. 线程通信:
线程通信是指多个线程之间通过某种方式进行数据的交换和协调,以实现线程间的合作和同步。线程通信的目的通常是实现线程间的数据共享和协作,避免竞态条件和死锁。
在Java中,常用的线程通信方式有以下几种:
1). 共享变量:多个线程共享同一个变量,通过读写该变量来实现线程间的信息传递和同步。为了保证线程安全,可以使用关键字synchronized或使用锁机制(如ReentrantLock)来对共享变量进行保护。
示例代码:
class SharedData {
private int data;
private boolean isReady = false;
public synchronized void writeData(int newData) {
// 写入新数据
data = newData;
// 设置标志位为true,表示数据已准备好
isReady = true;
// 唤醒等待的读线程
notify();
}
public synchronized int readData() throws InterruptedException {
// 如果数据还没准备好,则等待
while (!isReady) {
wait();
}
// 读取数据
int result = data;
// 重置标志位,表示数据已被消费
isReady = false;
return result;
}
}
class WriterThread extends Thread {
private SharedData sharedData;
public WriterThread(SharedData sharedData) {
this.sharedData = sharedData;
}
public void run() {
// 生成新数据
int newData = 10;
// 写入数据
sharedData.writeData(newData);
}
}
class ReaderThread extends Thread {
private SharedData sharedData;
public ReaderThread(SharedData sharedData) {
this.sharedData = sharedData;
}
public void run() {
try {
// 读取数据
int data = sharedData.readData();
System.out.println("Read data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
SharedData sharedData = new SharedData();
WriterThread writerThread = new WriterThread(sharedData);
ReaderThread readerThread = new ReaderThread(sharedData);
writerThread.start();
readerThread.start();
}
}
在上述示例中,通过共享变量data和isReady实现线程间的信息传递。写线程通过调用共享对象的`writeData()`方法写入新数据,并唤醒等待的读线程;读线程通过`readData()`方法等待数据准备好后读取数据。
2). 管道(Piped)流:使用PipedInputStream和PipedOutputStream来实现线程间的单向通信。一个线程使用PipedOutputStream将数据写入管道,另一个线程使用PipedInputStream从管道中读取数据。
示例代码:
class WriterThread extends Thread {
private PipedOutputStream pipedOutputStream;
public WriterThread(PipedOutputStream pipedOutputStream) {
this.pipedOutputStream = pipedOutputStream;
}
public void run() {
try {
// 写入数据
pipedOutputStream.write(10);
pipedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReaderThread extends Thread {
private PipedInputStream pipedInputStream;
public ReaderThread(PipedInputStream pipedInputStream) {
this.pipedInputStream = pipedInputStream;
}
public void run() {
try {
// 读取数据
int data = pipedInputStream.read();
System.out.println("Read data: " + data);
pipedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
PipedInputStream pipedInputStream = new PipedInputStream();
PipedOutputStream pipedOutputStream = new PipedOutputStream();
try {
// 连接输入流和输出流
pipedInputStream.connect(pipedOutputStream);
} catch (IOException e) {
e.printStackTrace

本文详细介绍了Java中多线程编程的概念、创建和启动线程的方式、线程同步和互斥、线程通信、线程调度、线程池、线程安全性和线程局部变量、线程安全的集合类、并发工具类、多线程性能调优以及并行计算和分布式计算等。通过示例代码展示了如何使用synchronized关键字、Lock接口、线程通信方法、线程池和并发工具类等,帮助理解多线程编程的关键点和实践技巧。
最低0.47元/天 解锁文章
979

被折叠的 条评论
为什么被折叠?



