java多线程(超详细!)

cnblogs 2024-07-30 08:09:01 阅读 96

Java 的多线程是一种允许在一个程序中同时运行多个线程的技术。每个线程是独立的执行路径,可以并发执行,从而提高程序的效率和响应能力。

1. 线程基础

Java 中的线程可以通过继承 <code>Thread 类或实现 Runnable 接口来创建和管理。

1.1 继承 Thread

class MyThread extends Thread {

public void run() {

System.out.println("Thread is running...");

}

}

public class TestThread {

public static void main(String[] args) {

MyThread t1 = new MyThread();

t1.start(); // 启动线程

}

}

run() 方法包含线程执行的代码,而 start() 方法用于启动新线程。

1.2 实现 Runnable 接口

class MyRunnable implements Runnable {

public void run() {

System.out.println("Thread is running...");

}

}

public class TestRunnable {

public static void main(String[] args) {

MyRunnable myRunnable = new MyRunnable();

Thread t1 = new Thread(myRunnable);

t1.start(); // 启动线程

}

}

通过实现 Runnable 接口的方式可以更灵活地共享资源。

2. 线程的生命周期

线程在其生命周期中经历以下状态:

  • 新建(New): 线程对象被创建,但还没有调用 start() 方法。
  • 就绪(Runnable): 线程对象调用了 start() 方法,等待 CPU 调度。
  • 运行(Running): 线程获得 CPU,开始执行 run() 方法的代码。
  • 阻塞(Blocked): 线程因为某种原因(如等待资源、睡眠)被挂起。
  • 死亡(Dead): 线程执行完 run() 方法,或者因异常退出。

3. 线程控制

Java 提供了一些方法来控制线程的执行:

  • sleep(long millis):让当前线程睡眠指定的毫秒数。
  • join():等待该线程终止,也就是说等待当前的线程结束, 才会继续执行下面的代码
  • yield():暂停当前线程,让出 CPU 给其他线程。
  • interrupt():中断线程。

4. 线程同步

多线程程序中可能会出现多个线程同时访问共享资源的情况,导致数据不一致的问题。为了解决这个问题,可以使用同步技术。

4.1. ReentrantLock

ReentrantLock 提供了比 synchronized 更加灵活的锁机制,可以显式地锁定和解锁。

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {

private final Lock lock = new ReentrantLock();

private int count = 0;

public void increment() {

lock.lock();

try {

count++;

} finally {

lock.unlock();

}

}

public int getCount() {

return count;

}

}

4.2. ReadWriteLock

ReadWriteLock 提供了一对锁,一个用于读操作,一个用于写操作。这允许多个读线程同时访问共享资源,但在写线程访问时会独占锁。

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {

private final ReadWriteLock lock = new ReentrantReadWriteLock();

private int count = 0;

public void increment() {

lock.writeLock().lock();

try {

count++;

} finally {

lock.writeLock().unlock();

}

}

public int getCount() {

lock.readLock().lock();

try {

return count;

} finally {

lock.readLock().unlock();

}

}

}

4.3 synchronized

在方法前使用 synchronized 关键字,确保同一时间只有一个线程可以执行该方法。

class Counter {

private int count = 0;

public synchronized void increment() {

count++;

}

public int getCount() {

return count;

}

}

4.4 同步块

同步块可以更灵活地控制需要同步的代码块,而不是整个方法。

class Counter {

private int count = 0;

public void increment() {

synchronized (this) {

count++;

}

}

public int getCount() {

return count;

}

}

4.5 注意事项

下面代码中的写法, 不能保证同一时间只有一个线程可以执行该方法。

因为 4.3 和 4.4 中 synchronized 的写法是根据 this 来保证同一时间只有一个线程可以执行, 但是他们的 this 是不同的。(把 "需要替换的" 换成注释上的就可以 "保证同一时间只有一个线程可以执行")

class Worker implements Runnable {

public static int cnt = 0;

@Override

public synchronized void run() {

for (int i = 0; i < 100000; i ++) {

cnt ++;

}

}

}

public class Main {

public static void main(String[] args) throws InterruptedException {

// Worker worker = new Worker();

// Thread t1 = new Thread(worker);

// Thread t2 = new Thread(worker);

Thread t1 = new Thread(new Worker());// 需要替换

Thread t2 = new Thread(new Worker());// 需要替换

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(Worker.cnt);

}

}

当然, 4.4 的代码也可以写成下面这样,这样他就是根据 lock 这个对象来保证同步的, java中的对象都可以当作lock

class Counter {

public static final Object lock = new Object();

private int count = 0;

public void increment() {

synchronized (lock ) {

count++;

}

}

public int getCount() {

return count;

}

}

5. 线程通信

Java 提供了 wait(), notify(), notifyAll() 方法来实现线程间的通信。

  • wait():让当前线程等待,直到其他线程调用 notify()notifyAll()
  • notify():唤醒一个正在等待的线程。
  • notifyAll():唤醒所有正在等待的线程。

6. 线程池

使用线程池可以有效地管理和复用线程,减少创建和销毁线程的开销。Java 提供了 Executor 框架来管理线程池。

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class TestThreadPool {

public static void main(String[] args) {

ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {

executor.execute(new MyRunnable());

}

executor.shutdown();

}

}

7. 高级线程工具

Java 提供了很多高级线程工具,如 Semaphore, CountDownLatch, CyclicBarrier 等,用于复杂的线程协调和同步。

7.1 Semaphore

信号量控制同时访问特定资源的线程数量。

import java.util.concurrent.Semaphore;

public class TestSemaphore {

private static final Semaphore semaphore = new Semaphore(3);

public static void main(String[] args) {

for (int i = 0; i < 10; i++) {

new Thread(new Task()).start();

}

}

static class Task implements Runnable {

public void run() {

try {

semaphore.acquire();

System.out.println("Thread " + Thread.currentThread().getName() + " is accessing the resource.");

Thread.sleep(2000);

semaphore.release();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

7.2 CountDownLatch

允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {

private static final int THREAD_COUNT = 3;

private static final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);

public static void main(String[] args) {

for (int i = 0; i < THREAD_COUNT; i++) {

new Thread(new Task()).start();

}

try {

latch.await(); // 主线程等待所有子线程完成

System.out.println("All threads have finished.");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

static class Task implements Runnable {

public void run() {

System.out.println("Thread " + Thread.currentThread().getName() + " is working.");

latch.countDown(); // 计数器减一

}

}

}

结论

Java 多线程是一个强大且复杂的技术,需要深入理解和小心使用,以避免潜在的并发问题和死锁情况。通过合理地利用线程同步、线程通信和线程池等工具,可以编写高效且安全的多线程应用程序。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。