Java多线程编程:基础创建方式与同步机制

本文详细阐述了Java编程中的多线程基础,包括线程概念、创建方式(继承Thread、实现Runnable和Callable接口)、线程同步与通信(synchronized、Lock接口及等待/通知机制)。实例演示了如何在实践中应用这些概念来提高程序效率。
摘要由CSDN通过智能技术生成

一、引言

在Java编程中,多线程是一个非常重要的概念。它允许我们同时执行多个任务,从而提高程序的执行效率和响应速度。本文将介绍Java多线程的基本概念、创建方式以及线程间的同步与通信,并通过示例代码进行说明。

二、Java多线程基本概念

    线程(Thread):线程是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以包含多个线程,它们共享进程的地址空间和系统资源。
    并发(Concurrency):并发是指两个或多个线程在同一时间段内交替执行的过程。由于线程切换的时间非常短,因此从用户的角度来看,多个线程似乎是同时执行的。
    并行(Parallelism):并行是指两个或多个线程在同一时刻内同时执行的过程。这通常需要多个处理器核心或超线程技术的支持。

三、Java多线程的创建方式

  •  继承Thread类

通过继承Thread类并重写其run()方法,可以创建一个新的线程。在run()方法中编写线程要执行的代码。然后,通过调用线程的start()方法启动线程。

示例代码:

public class MyThread extends Thread {  
    @Override  
    public void run() {  
        for (int i = 0; i < 10; i++) {  
            System.out.println("线程 " + Thread.currentThread().getId() + " 执行第 " + i + " 次任务");  
        }  
    }  
 
    public static void main(String[] args) {  
        MyThread t1 = new MyThread();  
        MyThread t2 = new MyThread();  
        t1.start();  
        t2.start();  
    }  

}
  •     实现Runnable接口

除了继承Thread类,Java还提供了另一种创建线程的方式,即实现Runnable接口。Runnable接口中只有一个run()方法,需要由实现类来重写。然后,将实现类的对象作为参数传递给Thread类的构造函数,创建Thread对象,并调用其start()方法启动线程。

示例代码:

public class MyRunnable implements Runnable {  
    @Override  
    public void run() {  
        for (int i = 0; i < 10; i++) {  
            System.out.println("线程 " + Thread.currentThread().getId() + " 执行第 " + i + " 次任务");  
        }  
    }  
 
    public static void main(String[] args) {  
        MyRunnable task = new MyRunnable();  
        Thread t1 = new Thread(task);  
        Thread t2 = new Thread(task);  
        t1.start();  
        t2.start();  
    }  

}
  • 实现Callable接口并使用FutureTask

除了继承Thread类和实现Runnable接口,Java还提供了Callable接口来创建线程。Callable接口与Runnable接口相似,但Callable允许有返回值,并且可以抛出异常。为了使用Callable,我们需要将其封装成FutureTask对象,然后将其传递给Thread对象。

示例代码:

import java.util.concurrent .*;


public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable task = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        Thread t1 = new Thread(futureTask);

        t1.start();
        // 等待线程执行完成并获取结果
        Integer result = futureTask.get();
        System.out.println("线程计算结果为:" + result);
    }
}

在这个例子中,我们创建了一个实现了Callable接口的MyCallable类,它返回从0到99的整数和。然后,我们将MyCallable对象封装成FutureTask对象,并将FutureTask对象传递给Thread对象。当线程执行完成后,我们可以通过调用FutureTask的get()方法来获取线程的执行结果。

四、线程间的同步与通信

当多个线程访问共享资源时,可能会出现数据不一致的问题。为了解决这个问题,Java提供了多种同步机制,如synchronized关键字、Lock接口等。此外,线程间还可以通过等待/通知机制进行通信。

synchronized关键字

synchronized关键字可以用于修饰方法或代码块,以确保同一时间只有一个线程可以执行被修饰的代码。当一个线程进入synchronized方法或代码块时,其他尝试进入的线程将被阻塞,直到当前线程退出synchronized方法或代码块。

Lock接口

除了synchronized关键字,Java还提供了Lock接口来实现更灵活的同步控制。Lock接口提供了更丰富的同步方法,如lock()、unlock()、tryLock()等。此外,Lock接口还支持中断等待的线程、尝试获取锁等高级功能。

等待/通知机制详解

Java中的等待/通知机制是基于对象锁的,它允许线程之间进行协作。当一个线程需要等待某个条件成立时,它可以调用对象的wait()方法进入等待状态,并释放持有的锁。其他线程在修改条件后,可以调用对象的notify()或notifyAll()方法来唤醒等待的线程。 wait() 方法

 当线程调用对象的wait()方法时,它会立即释放该对象的锁,并进入等待状态。
 等待状态中的线程不会消耗CPU资源,直到被其他线程唤醒。
 调用wait()方法后,线程必须重新获取锁才能继续执行。

notify() 方法

 当一个线程调用对象的notify()方法时,它会随机唤醒该对象等待池中等待的一个线程(如果等待池中有线程的话)。
 被唤醒的线程会重新尝试获取对象的锁,如果此时锁已被其他线程持有,则它将被阻塞,直到获得锁。

notifyAll() 方法

 notifyAll()方法与notify()方法类似,但会唤醒等待池中等待的所有线程。
 唤醒后的线程会按照竞争锁的规则重新竞争,即它们都需要重新获取对象的锁才能继续执行。

示例

下面是一个简单的示例,展示了如何使用等待/通知机制实现线程间的协作:

public class WaitNotifyExample { private int count = 0; private final Object lock = new Object();

 public void increment() {  
     synchronized (lock) {  
         while (count == 5) {  
             try {  
                 lock.wait(); // 当前线程等待,释放锁  
             } catch (InterruptedException e) {  
                 e.printStackTrace();  
             }  
         }  
         count++;  
         System.out.println("Count: " + count);  
         lock.notifyAll(); // 唤醒等待的线程  
     }  
 }  
   
 public void decrement() {  
     synchronized (lock) {  
         while (count == 0) {  
             try {  
                 lock.wait(); // 当前线程等待,释放锁  
             } catch (InterruptedException e) {  
                 e.printStackTrace();  
             }  
         }  
         count--;  
         System.out.println("Count: " + count);  
         lock.notifyAll(); // 唤醒等待的线程  
     }  
 }  
   
 public static void main(String[] args) {  
     WaitNotifyExample example = new WaitNotifyExample();  
   
     // 创建两个线程,一个增加count,一个减少count  
     Thread incrementThread = new Thread(() -> {  
         for (int i = 0; i < 10; i++) {  
             example.increment();  
             try {  
                 Thread.sleep(100); // 模拟耗时操作  
             } catch (InterruptedException e) {  
                 e.printStackTrace();  
             }  
         }  
     });  
   
     Thread decrementThread = new Thread(() -> {  
         for (int i = 0; i < 10; i++) {  
             example.decrement();  
             try {  
                 Thread.sleep(100); // 模拟耗时操作  
             } catch (InterruptedException e) {  
                 e.printStackTrace();  
             }  
         }  
     });  
   
     incrementThread.start();  
     decrementThread.start();  
 }  

}

在这个示例中,我们创建了一个WaitNotifyExample类,它包含一个计数器count和一个锁对象lock。increment()方法用于增加count的值,decrement()方法用于减少count的值。在修改count的值之前,两个方法都会检查count的值,如果达到了某个条件(count == 5或count == 0),则调用wait()方法使当前线程等待,并释放锁。其他线程在修改count的值后,会调用notifyAll()方法唤醒等待的线程。这样就实现了线程间的协作和同步。

五、总结

本文介绍了Java多线程的基本概念、三种主要的创建方式(继承Thread类、实现Runnable接口、实现Callable接口并使用FutureTask)以及线程间的同步与通信。在实际开发中,我们需要根据具体需求选择合适的线程创建方式和同步机制,以确保程序的正确性和性能。特别是当线程需要返回结果时,使用Callable接口和FutureTask对象是一个很好的选择。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值