Java 多线程编程:9.1 线程的创建与运行

多线程编程是Java中一个重要的特性,它允许程序同时执行多个线程,从而提高程序的效率和响应能力。在本节中,我们将深入探讨Java中线程的创建与运行,包括不同的创建方式、优缺点、注意事项以及示例代码。

1. 线程的基本概念

在Java中,线程是程序执行的最小单位。每个线程都有自己的执行路径,多个线程可以并发执行。Java通过java.lang.Thread类和java.lang.Runnable接口来实现多线程。

2. 创建线程的方式

Java中创建线程主要有两种方式:

2.1 继承Thread类

通过继承Thread类来创建线程是最简单的方式。我们只需创建一个新的类,继承Thread类,并重写run()方法。

示例代码:

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            try {
                Thread.sleep(500); // 让线程休眠500毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        thread1.start(); // 启动线程1
        thread2.start(); // 启动线程2
    }
}

优点:

  • 简单易懂,适合初学者。
  • 直接使用Thread类的功能。

缺点:

  • Java只支持单继承,因此如果一个类已经继承了其他类,就不能再继承Thread类。
  • 线程的功能和业务逻辑混合在一起,可能导致代码不够清晰。

注意事项:

  • run()方法中不要直接调用start()方法,否则会导致IllegalThreadStateException异常。

2.2 实现Runnable接口

实现Runnable接口是另一种创建线程的方式。这种方式更灵活,因为它允许我们将线程的逻辑与业务逻辑分开。

示例代码:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            try {
                Thread.sleep(500); // 让线程休眠500毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);
        
        thread1.start(); // 启动线程1
        thread2.start(); // 启动线程2
    }
}

优点:

  • 可以实现多重继承,允许类继承其他类。
  • 线程逻辑与业务逻辑分离,代码更清晰。
  • 可以共享同一个Runnable实例,适合需要共享数据的场景。

缺点:

  • 需要额外创建一个Runnable对象,稍显复杂。

注意事项:

  • 共享Runnable实例时,需注意线程安全问题,避免数据竞争。

3. 线程的生命周期

线程的生命周期包括以下几个状态:

  1. 新建状态(New):线程被创建,但尚未启动。
  2. 就绪状态(Runnable):线程准备好运行,但尚未被CPU调度。
  3. 运行状态(Running):线程正在执行。
  4. 阻塞状态(Blocked):线程因等待某个资源而被阻塞。
  5. 死亡状态(Terminated):线程执行完毕或因异常终止。

4. 线程的调度

Java线程的调度是由JVM控制的,通常使用时间片轮转算法。线程的优先级可以通过setPriority(int priority)方法设置,优先级范围为1到10,默认值为5。

示例代码:

public class ThreadPriorityExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 1 - Count: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 2 - Count: " + i);
            }
        });

        thread1.setPriority(Thread.MIN_PRIORITY); // 设置最低优先级
        thread2.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级

        thread1.start();
        thread2.start();
    }
}

5. 线程的安全性

在多线程环境中,线程安全是一个重要的问题。多个线程同时访问共享资源时,可能会导致数据不一致或程序崩溃。为了解决这个问题,可以使用以下几种方法:

  • 同步(Synchronized):使用synchronized关键字来保证同一时间只有一个线程可以访问某个资源。
  • Lock接口:使用java.util.concurrent.locks.Lock接口提供更灵活的锁机制。
  • 原子变量:使用java.util.concurrent.atomic包中的原子变量类,确保操作的原子性。

示例代码(使用synchronized):

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizedExample {
    public static void main(String[] args) {
        Counter counter = new Counter();
        
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final Count: " + counter.getCount());
    }
}

6. 总结

在本节中,我们详细探讨了Java中线程的创建与运行,包括继承Thread类和实现Runnable接口的两种方式。我们还讨论了线程的生命周期、调度和安全性等重要概念。通过示例代码,我们展示了如何在实际应用中使用这些概念。

注意事项总结:

  • 选择合适的线程创建方式,考虑到代码的可读性和可维护性。
  • 处理线程安全问题,避免数据竞争和不一致性。
  • 理解线程的生命周期和调度机制,以便更好地控制线程的行为。

多线程编程是一个复杂但强大的工具,掌握它将极大地提升你的Java编程能力。希望本节的内容能帮助你更深入地理解Java中的多线程编程。