Java 中,线程(Thread) 是程序执行的基本单位,每个线程都代表着程序中的一个独立的执行流。Java 提供了丰富的 API 来支持多线程编程,通过 Thread 类和 Runnable 接口可以创建和管理线程。Java 的线程机制非常强大,支持多线程的创建、管理、同步等操作。在进行多线程编程时,需要特别注意线程安全、同步以及性能优化等方面。通过合理地使用线程,可以提高程序的效率和响应速度。本文主要介绍Java 线程(Thread)。

1、Java 线程(Thread)

线程允许程序通过同时执行多项操作来更有效地运行。

线程可用于在后台执行复杂的任务,而不会中断主程序。

2、创建线程Thread

 Java 中,线程(Thread)是程序执行的最小单位,Java 提供了强大的并发编程支持,可以通过 继承 Thread 类 或 实现 Runnable 接口 来创建线程。

可以通过扩展Thread类并覆盖其run()方法来创建它:

1)继承 Thread 类

public class Main {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start(); // 启动线程
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
    }
}

注意: 不能直接调用 run() 方法,而应使用 start() 让 JVM 创建新线程并调用 run()

2)实现 Runnable 接口

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
    }
}

优点:比继承 Thread 更灵活,因为 Java 只支持单继承,而 Runnable 接口可以被多个类实现。

3)使用 Lambda 表达式

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行"));
        t1.start();
    }
}

3、运行线程

如果该类使用extends扩展了Thread类,则可以通过创建该类的实例并调用其start()方法来运行该线程:

例如

public class Main extends Thread {
  public static void main(String[] args) {
    Main thread = new Main();
    thread.start();
    System.out.println("thread线程外运行");
  }
  public void run() {
    System.out.println("thread线程中运行");
  }
}

如果该类使用implements实现了Runnable接口,则可以通过将类的实例传递给Thread对象的构造函数,然后调用该线程的start()方法:

例如

public class Main implements Runnable {
  public static void main(String[] args) {
    Main obj = new Main();
    Thread thread = new Thread(obj);
    thread.start();
    System.out.println("thread线程外运行");
  }
  public void run() {
    System.out.println("thread线程中运行");
  }
}

使用extends的线程与使用implements的线程之间的区别主要区别在于,当一个类扩展Thread类时,不能扩展任何其他类,但是通过实现Runnable接口,可以从 另一个类,例如classMyClass扩展实现了Runnable接的OtherClass

4、线程的方法

方法作用
start()启动线程并调用 run()
run()线程执行的逻辑
join()等待当前线程执行完毕
sleep(ms)让当前线程睡眠一段时间
yield()让出 CPU 资源,让其他线程运行
interrupt()中断线程

join() 让主线程等待子线程执行完毕:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("子线程执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t1.join(); // 主线程等待 t1 执行完
        System.out.println("主线程继续执行");
    }
}

5、线程并发问题

因为线程与程序的其他部分同时运行,所以无法知道代码将以什么顺序运行。当线程和主程序正在读取和写入相同的变量时,这些值是不可预测的。由此产生的问题称为并发问题。

例如:

一个代码示例,其中amount的值不可预测:

public class Main {
  public static void main(String[] args) {
  MyThread thread = new MyThread();
  thread.start();

  // 更新输出值
  System.out.println("Main begin: " + MyThread.amount);
  MyThread.amount++;
  System.out.println("Main end: " + MyThread.amount);
 }
  
}

class MyThread extends Thread {
  public static int amount = 0;
  public  void run() {
    amount++;
  }
}

为避免并发问题,最好在线程之间共享尽可能少的属性。 如果需要共享属性,则一种可能的解决方案是使用线程的isAlive()方法在使用任何可以更改的属性之前检查线程是否已完成运行。

例如:

使用isAlive()防止并发问题:

public class Main {
  public static void main(String[] args) {
  MyThread thread = new MyThread();
  thread.start();
   // 等待线程运行完成
    while(thread.isAlive()) {
    System.out.println("Waiting...");
  }
  // 更新输出值
  System.out.println("Main begin: " + MyThread.amount);
  MyThread.amount++;
  System.out.println("Main end: " + MyThread.amount);
 }
  
}
class MyThread extends Thread {
  public static int amount = 0;
  public  void run() {
    amount++;
  }
}

另外还可以加锁的方式,解决并发问题,例如,Java中线程的共享互斥操作,会使用synchronized关键字。每个锁在同一时刻,只能由一个线程持有。例如

public class Main {
  public static void main(String[] args) {
  MyThread thread = new MyThread();
  thread.start();
  // 更新并输出值 
  synchronized (MyThread.class) {
  System.out.println("Main begin: " + MyThread.amount);
  MyThread.amount++;
  System.out.println("Main end: " + MyThread.amount);
  }
 }
  
}

class MyThread extends Thread {
  public static int amount = 0;
  public  void run() {
  synchronized (MyThread.class) {
    amount++;
   }
  }
}

注意synchronized方法或声明执行期间,如程序遇到任何异常或return,线程都会释放锁。

ReentrantLock 提供了更灵活的锁机制,支持可重入、超时等待、尝试锁定等功能。

import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("最终计数值: " + counter.getCount());
    }
}

class Counter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

推荐文档