多线程是Java实现并发执行的核心机制,它允许程序同时运行多个独立的执行流(线程),从而提高 CPU 利用率,适用于需要处理多个任务的场景(如网络请求处理、数据并行计算等)。多线程共享资源时可能出现数据不一致问题,线程同步技术正是解决这一问题的关键。了解多线程的创建方法和同步关键字的使用,是掌握并发编程的基础。
一、Java 多线程的创建方法
Java 中创建线程主要有三种方式:继承Thread类、实现Runnable接口、实现Callable接口(带返回值),其中前两种最为常用。
(一)继承 Thread 类
Thread类是 Java 线程的基础类,继承该类并重写run()方法即可定义线程任务,通过start()方法启动线程(而非直接调用run())。
示例:
// 自定义线程类class MyThread extends Thread { @Override public void run() { // 线程执行的任务 for (int i = 0; i < 5; i++) { System.out.println("线程" + Thread.currentThread().getName() + ":" + i); } }}// 测试public class ThreadTest { public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.setName("Thread-1"); thread2.setName("Thread-2"); thread1.start(); // 启动线程1 thread2.start(); // 启动线程2 }}
特点:实现简单,但因 Java 单继承限制,该类无法再继承其他类,灵活性较低。
(二)实现 Runnable 接口
Runnable接口仅定义了run()方法,实现该接口并重写方法后,需将实例传入Thread类的构造方法启动线程。
示例:
// 实现Runnable接口class MyRunnable implements Runnable { @Override public void run() { // 线程执行的任务 for (int i = 0; i < 5; i++) { System.out.println("线程" + Thread.currentThread().getName() + ":" + i); } }}// 测试public class RunnableTest { public static void main(String[] args) { MyRunnable task = new MyRunnable(); Thread thread1 = new Thread(task, "Thread-1"); Thread thread2 = new Thread(task, "Thread-2"); thread1.start(); thread2.start(); }}
特点:避免单继承限制,多个线程可共享同一个Runnable实例(适合多线程共享资源场景),是更推荐的方式。
(三)实现 Callable 接口(带返回值)
Callable接口与Runnable类似,但call()方法可返回结果并抛出异常,需结合FutureTask类获取返回值。
示例:
import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;// 实现Callable接口class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 10; i++) { sum += i; } return sum; // 返回计算结果 }}// 测试public class CallableTest { public static void main(String[] args) throws Exception { MyCallable task = new MyCallable(); FutureTask<Integer> futureTask = new FutureTask<>(task); Thread thread = new Thread(futureTask); thread.start(); System.out.println("计算结果:" + futureTask.get()); // 获取返回值 }}
特点:适合需要线程返回结果的场景(如并行计算),但实现稍复杂。
二、线程同步的关键字:synchronized
多线程共享资源时,若多个线程同时修改资源,可能导致数据错误(如银行账户并发取款)。synchronized关键字是 Java 中最基础的线程同步机制,通过加锁保证同一时间只有一个线程执行特定代码块,从而保护共享资源。
(一)synchronized 的使用方式
修饰方法:锁住当前对象实例(非静态方法)或类(静态方法)。
class Counter { private int count = 0; // 非静态同步方法:锁住当前Counter实例 public synchronized void increment() { count++; } // 静态同步方法:锁住Counter类 public static synchronized void staticMethod() { // 静态资源操作 }}
修饰代码块:锁住指定对象(更灵活,可减少锁范围)。
class Counter { private int count = 0; private Object lock = new Object(); // 自定义锁对象 public void increment() { // 同步代码块:锁住lock对象 synchronized (lock) { count++; } }}
(二)同步原理
synchronized通过对象监视器(Monitor) 实现锁机制:
线程进入同步代码前需获取锁,执行完毕后释放锁;
若锁被其他线程占用,当前线程会进入阻塞状态,直至锁释放。
这种机制保证了同步代码块的原子性(不可分割),避免多线程并发修改导致的数据不一致。
(三)适用场景
多线程共享简单资源(如计数器、共享变量);
对同步性能要求不极致的场景(synchronized在 JDK 1.6 后经过优化,性能已大幅提升)。
示例:解决并发计数问题
// 未同步的计数器(存在问题)class UnsafeCounter { private int count = 0; public void increment() { count++; } public int getCount() { return count; }}// 同步的计数器(正确)class SafeCounter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; }}// 测试public class SyncTest { public static void main(String[] args) throws InterruptedException { SafeCounter counter = new SafeCounter(); // 10个线程同时递增 for (int i = 0; i < 10; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { counter.increment(); } }).start(); } Thread.sleep(1000); // 等待所有线程执行完毕 System.out.println("最终计数:" + counter.getCount()); // 输出10000(正确) }}
三、使用多线程的注意事项
避免过度同步:同步范围过大(如同步整个方法)会导致线程阻塞时间长,降低并发效率,应尽量缩小同步代码块范围。
防止死锁:多线程持有多个锁时,若获取顺序不一致可能导致死锁(如线程 1 持有锁 A 等待锁 B,线程 2 持有锁 B 等待锁 A),需保证锁的获取顺序一致。
线程池优先:频繁创建和销毁线程会消耗资源,实际开发中建议使用线程池(如ExecutorService)管理线程生命周期。
Java 多线程的创建可根据需求选择继承Thread、实现Runnable或Callable,其中Runnable因灵活性更常用;synchronized关键字是实现线程同步的基础,通过加锁机制保护共享资源。掌握这些知识,能帮助开发者应对简单的并发场景,为学习更高级的并发工具(如Lock、Atomic类)奠定基础。