Java 多线程与并发编程·Java 工程师必知必会

Java 多线程与并发编程是 Java 工程师必须掌握的重要技能之一。随着计算机硬件性能的不断提升,多核 CPU 成为主流,开发高并发应用已成为越来越重要的需求。本文将介绍 Java 多线程与并发编程的基本概念和技术,包括线程、锁、同步机制、线程池、并发集合和并发编程的实践案例。

线程

线程是进程的一部分,是程序执行的最小单位。Java 中的线程是通过 Thread 类实现的。在 Java 中,线程有五个状态:新建状态、就绪状态、运行状态、阻塞状态和终止状态。通常使用 start 方法启动一个新线程,使用 join 方法等待线程执行完毕,使用 sleep 方法暂停线程的执行。以下是一个简单的线程实例:

public class MyThread extends Thread {
  @Override
  public void run() {
      System.out.println("Hello from thread " + Thread.currentThread().getName());
  }
  public static void main(String[] args) {
      MyThread thread1 = new MyThread();
      MyThread thread2 = new MyThread();
      thread1.start();
      thread2.start();
  }
}

锁和同步机制

  • 在多线程编程中,需要使用锁和同步机制来保证线程安全。

  • Java 中提供了多种锁和同步机制,包括 synchronized 关键字、ReentrantLock 类、CountDownLatch 类、Semaphore 类等。

  • synchronized 关键字是 Java 中最常用的同步机制,可以用来保护临界区资源的访问,防止多个线程同时访问造成数据不一致的问题。以下是一个使用 synchronized 实现线程安全的计数器的例子:

public class Counter {
   private int count;
   public synchronized void increment() {
       count++;
  }
   public synchronized int getCount() {
       return count;
  }
}

ReentrantLock 类是一个可重入锁,它提供了与 synchronized 关键字相似的同步功能,但更加灵活和可控。

以下是一个使用 ReentrantLock 实现线程安全的计数器的例子:

public class Counter {
   private int count;
   private ReentrantLock lock = new ReentrantLock();

   public void increment() {
       lock.lock();
       try {
           count++;
      } finally {
           lock.unlock();
      }
  }

   public int getCount() {
       lock.lock();
       try {
           return count;
      } finally {
           lock.unlock();
      }
  }
}

线程池

线程池是多线程编程中的一种常用技术,它可以管理和复用线程,提高程序的性能和可维护性。

Java 中提供了 Executor 框架和 ThreadPoolExecutor 类来实现线程池。以下是一个使用 ThreadPoolExecutor 实现的线程池例子:

public public class ThreadPoolExample {
   public static void main(String[] args) {
       ExecutorService executor = Executors.newFixedThreadPool(2);
       for (int i = 0; i < 10; i++) {
           executor.execute(new WorkerThread("Thread " + i));
      }
       executor.shutdown();
       while (!executor.isTerminated()) {
           // 等待线程池中的任务执行完毕
      }
       System.out.println("All threads have been terminated.");
  }
}

class WorkerThread implements Runnable {
   private String name;
   public WorkerThread(String name) {
       this.name = name;
  }

   @Override
   public void run() {
       System.out.println("Start executing thread " + name);
       try {
           Thread.sleep(1000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println("Finish executing thread " + name);
  }
}

并发集合

Java 提供了一些并发集合类,例如 ConcurrentHashMap、ConcurrentLinkedQueue、CopyOnWriteArrayList 等,用于在多线程环境下安全地读写数据。这些集合类采用了一些特殊的技术,例如分段锁、CAS(Compare And Swap)等,来保证线程安全。

以下是一个使用 ConcurrentHashMap 实现的线程安全的计数器例子:

public class ConcurrentCounter {
   private ConcurrentHashMap<String, Integer> counter = new ConcurrentHashMap<>();
   public void increment(String key) {
       counter.compute(key, (k, v) -> v == null ? 1 : v + 1);
  }

   public int getCount(String key) {
       return counter.get(key);
  }
}

并发编程实践

下面介绍一个简单的并发编程实践:多线程下载器。该下载器使用多个线程同时下载一个文件,从而加快下载速度。

public class MultiThreadDownloader {
   private URL url;
   private int threadCount;
   public MultiThreadDownloader(String urlString, int threadCount) throws MalformedURLException {
       this.url = new URL(urlString);
       this.threadCount = threadCount;
  }

   public void download() throws IOException, InterruptedException {
       HttpURLConnection connection = (HttpURLConnection) url.openConnection();
       int fileSize = connection.getContentLength();
       connection.disconnect();
       int blockSize = fileSize / threadCount + 1;
       CountDownLatch latch = new CountDownLatch(threadCount);
       for (int i = 0; i < threadCount; i++) {
           int start = i * blockSize;
           int end = Math.min((i + 1) * blockSize - 1, fileSize - 1);
           new DownloadThread(url, start, end, latch).start();
      }
       latch.await();
       System.out.println("Download completed.");
  }
   public static void main(String[] args) throws IOException, InterruptedException {
       MultiThreadDownloader downloader = new MultiThreadDownloader("https://www.example.com/file.txt", 4);
       downloader.download();
  }
}
class DownloadThread extends Thread {
   private URL url;
   private int start;
   private int end;
   private CountDownLatch latch;

   public DownloadThread(URL url, int start, int end, CountDownLatch latch) {
       this.url = url;
       this.start = start;
       this.end = end;
       this.latch = latch;
  }
   @Override
   public void run() {
       try {
           HttpURLConnection connection = (HttpURLConnection) url.openConnection();
           connection.setRequestProperty("Range", "bytes=" + start + "-" + end);
           InputStream in = connection.getInputStream();
           FileOutputStream out = new FileOutputStream("file.txt", true);
           byte[] buffer = new byte[4096];
           int len;
           while ((len = in.read(buffer)) != -1) {
               out.write(buffer, 0, len);
          }
           in.close();
           out.close();
           connection.disconnect();
           System.out.println("Downloaded bytes " + start + "-" + end + ".");
      } catch (IOException e) {
           e.printStackTrace();
      } finally {
           latch.countDown();
      }
  }
}
  • 在主函数中,我们创建了一个 MultiThreadDownloader 对象,指定了下载文件的 URL 和线程数。

  • download 方法中,首先发送一个 HEAD 请求获取文件大小,然后根据线程数计算出每个线程下载的数据块大小。

  • 接下来,使用 CountDownLatch 来控制所有线程的执行,创建 threadCount 个 DownloadThread 对象,并启动它们。

  • 每个 DownloadThread 对象都指定了下载数据的起始位置和终止位置,以及一个 CountDownLatch 对象。

  • DownloadThread 的 run 方法中,首先发送一个 GET 请求,指定下载数据的范围,然后将数据写入本地文件,并计数器减一。最后在主函数中等待所有线程执行完毕。

总结

本文简单介绍了 Java 多线程与并发编程的基础知识和常用技术。

在实际开发中,需要根据具体的场景选择合适的并发模型和工具,避免出现死锁、竞态条件等并发问题,从而实现高效、安全的多线程编程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值