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接口、线程同步、线程池以及线程安全是相对核心和常用的知识点。使用好这些多线程编程的基础知识,可以十分高效地完成开发任务,并在高并发场景下保证程序的运行效率和数据安全。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/194567.html
微信扫一扫
支付宝扫一扫