Java作为一种跨平台的编程语言,其多线程的特性使得它在并发处理的场景下十分重要。本文将从多个方面对Java多线程的实现方法进行阐述,包括线程的创建、Thread和Runnable接口、线程同步、线程池以及线程安全。
一、线程的创建
线程的创建是指创建一个独立的执行单元,切换到该执行单元可实现多任务并发执行。Java中线程的创建主要有两种方式:
1. 继承Thread类
public class MyThread extends Thread { public void run() { // 实现线程体代码 } } // 创建线程 MyThread myThread = new MyThread(); myThread.start();
通过继承Thread类并重写run()方法实现线程体,然后创建该类的实例并调用start()方法启动线程。
2. 实现Runnable接口
public class MyRunnable implements Runnable { public void run() { // 实现线程体代码 } } // 创建线程 Thread thread = new Thread(new MyRunnable()); thread.start();
通过实现Runnable接口并实现run()方法实现线程体,然后创建Thread类的实例并将该接口实例化作为参数传入,最后调用start()方法启动线程。
二、Thread和Runnable接口
Thread类和Runnable接口是Java中定义线程的两个基本方式,二者的区别主要在于:
1. 继承Thread类
优点:可以直接调用Thread类的方法,简单直接。
缺点:由于Java不支持多重继承,因此如果继承Thread类将会占用一个类的继承关系。
2. 实现Runnable接口
优点:可以避免单继承的限制,使得程序的扩展性更好。
缺点:不能直接调用Thread类的方法,需要创建Thread实例并将Runnable实例作为参数传入才能启动线程。
三、线程同步
多线程在执行时,如若对共享资源进行读取或修改,会出现数据不一致或者数据安全问题,为了避免这类问题,Java提供了synchronized关键字锁定代码块、实例方法和类方法。在Java使用synchronized关键字实现线程同步的方式有如下几种:
1. 同步代码块
synchronized (object) { // 访问共享资源代码 }
其中object为锁对象,同步代码块只能被同一把锁的线程访问,仅在执行完同步代码块时才释放锁。
2. 同步实例方法
public synchronized void method() { // 访问共享资源代码 }
对实例方法加synchronized关键字可以实现对整个方法的同步,同一时刻只能有一个线程访问该方法,其他线程需要等待。
3. 同步类方法
public static synchronized void method() { // 访问共享资源代码 }
对类方法加synchronized关键字可以实现对整个类的同步,同一时刻只能有一个线程访问该类的类方法,其他线程需要等待。
四、线程池
在Java中,线程池是用来管理线程的一种机制,它可以减少线程创建、上下文切换的开销,提高系统的运行效果。
Java提供了Executor和ExecutorService接口作为线程池的操作类,常用的线程池实现类有ThreadPoolExecutor、ScheduledThreadPoolExecutor等。
1. ThreadPoolExecutor
ThreadPoolExecutor是Java中线程池的基本实现,其构造方法参数较多:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
其中,corePoolSize指定了线程池的核心线程数量,maximumPoolSize指定了最大线程数量,当工作队列满时并且当前的线程数小于最大线程数时,线程池会新建线程。keepAliveTime和unit参数指定了空闲线程存活的时间,workQueue为任务队列,用于存放还没有执行的任务。threadFactory用于创建新线程,handler则是当线程池满了并且阻塞队列也满了时采取的拒绝处理策略。
2. ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,是一个可以周期性执行任务的线程池,常用于定时器等场景。
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler)
其中,corePoolSize和threadFactory参数与ThreadPoolExecutor类似,handler为拒绝策略。
五、线程安全
Java中线程安全是指多个线程同时执行某个方法或者代码块时,不会产生数据冲突、数据覆盖等安全问题。Java提供了多种线程安全的机制,其中常用的有volatile、synchronized关键字和Atomic包等。
1. volatile关键字
volatile关键字可以保证变量的可见性和禁止指令重排,但不具备互斥性,因此不能保证原子性。
public class VolatileTest { public volatile int count = 0; public void increase() { count++; } public static void main(String[] args) throws InterruptedException { final VolatileTest test = new VolatileTest(); for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { public void run() { test.increase(); } }).start(); } Thread.sleep(1000); System.out.println(test.count); } }
2. synchronized关键字
synchronized关键字可以保证临界区代码的原子性和可见性,但会降低程序的并发性。
public class SynchronizedTest { public int count = 0; public synchronized void increase() { count++; } public static void main(String[] args) throws InterruptedException { final SynchronizedTest test = new SynchronizedTest(); for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { public void run() { test.increase(); } }).start(); } Thread.sleep(1000); System.out.println(test.count); } }
3. Atomic包
Atomic包提供了一系列的原子性操作类,包括AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等,可以保证变量的原子性和可见性。
public class AtomicTest { public AtomicInteger count = new AtomicInteger(0); public void increase() { count.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { final AtomicTest test = new AtomicTest(); for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { public void run() { test.increase(); } }).start(); } Thread.sleep(1000); System.out.println(test.count); } }
总结
在多线程编程中,线程的创建、Thread和Runnable接口、线程同步、线程池以及线程安全是相对核心和常用的知识点。使用好这些多线程编程的基础知识,可以十分高效地完成开发任务,并在高并发场景下保证程序的运行效率和数据安全。