为了账号安全,请及时绑定邮箱和手机立即绑定

Java高并发入门:轻松掌握并发编程基础

标签:
Java 架构
概述

本文详细介绍了JAVA高并发入门的相关知识,包括并发编程的基础概念、线程的创建与管理、以及线程同步与互斥的实现方法。通过实例代码,读者可以轻松掌握Java中的并发编程技巧,进而构建简单的高并发系统。

1. Java并发编程概述

什么是并发

并发是指同时执行多个任务的能力。在Java中,通过使用多线程技术来实现并发,使得程序可以在多个线程之间交替执行不同的任务,从而提高程序的执行效率和用户体验。

为什么需要学习Java并发编程

随着现代计算机硬件的发展,多核处理器已经成为主流。通过并发编程,可以充分利用多核处理器的计算能力,提高程序的运行效率和响应速度。此外,对于需要处理大量数据或高并发请求的应用场景,如Web服务器、分布式系统等,学习并发编程是必不可少的。

并发编程的常用术语和概念

  • 线程(Thread):线程是程序执行的一个独立路径,是操作系统独立调度和分派的基本单位。
  • 进程(Process):进程是程序的执行实例,包含了一段程序及其数据结构。
  • 线程安全(Thread-Safe):确保多个线程在访问共享资源时不会导致数据不一致或程序崩溃的特性。
  • 死锁(Deadlock):多个线程在无限等待其他线程释放资源的情况,导致程序无法继续执行。
  • 锁(Lock):一种同步机制,用于控制对共享资源的访问。
  • 同步(Synchronization):确保多个线程在访问共享资源时不会发生冲突的机制。
  • 信号量(Semaphore):用于限制访问共享资源的线程数量。

2. Java线程基础

线程的概念和用途

线程是进程内的一个执行单元,共享进程的资源。Java通过Thread类来创建和管理线程。每个线程都有自己的独立栈,线程之间可以共享进程的堆内存和静态变量。

创建和管理线程

通过继承Thread类或实现Runnable接口可以创建线程。下面展示了两种创建线程的方式:

  1. 继承Thread类:

    public class MyThread extends Thread {
       public MyThread(String name) {
           super(name);
       }
    
       @Override
       public void run() {
           for (int i = 0; i < 10; i++) {
               System.out.println(getName() + " running " + i);
               try {
                   Thread.sleep(1000); // 线程休眠1秒
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }
    }
    
    public class ThreadDemo {
       public static void main(String[] args) {
           MyThread thread = new MyThread("MyThread");
           thread.start();
       }
    }
  2. 实现Runnable接口:

    public class MyRunnable implements Runnable {
       @Override
       public void run() {
           for (int i = 0; i < 10; i++) {
               System.out.println(Thread.currentThread().getName() + " running " + i);
               try {
                   Thread.sleep(1000); // 线程休眠1秒
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }
    }
    
    public class RunnableDemo {
       public static void main(String[] args) {
           Thread thread = new Thread(new MyRunnable());
           thread.start();
       }
    }

线程的生命周期和状态

线程的生命周期包括创建、可运行、运行、阻塞、死亡五个阶段。

  • 创建(New):线程对象被创建,但尚未启动。
  • 可运行(Runnable):线程被操作系统调度到CPU中执行。
  • 运行(Running):线程占用CPU实际运行代码。
  • 阻塞(Blocked):线程等待资源释放(如锁)或等待I/O操作完成。
  • 死亡(Dead):线程执行完毕或被终止。

3. 线程同步与互斥

同步的概念和实现

同步是为了确保多个线程在访问共享资源时不会发生数据冲突。Java中提供了多种实现同步的方式,包括synchronized关键字和ReentrantLock类。

使用synchronized关键字实现同步

synchronized关键字可以应用于方法或代码块,用于防止多个线程同时访问同一个对象的共享资源。

public class SyncDemo {
    private int count = 0;

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

    public synchronized void decrement() {
        count--;
    }

    public synchronized int getCount() {
        return count;
    }

    public static void main(String[] args) {
        SyncDemo demo = new SyncDemo();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                demo.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                demo.decrement();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(demo.getCount()); // 应该输出0
    }
}

使用volatile关键字

volatile关键字用于声明变量,确保该变量在多个线程之间的可见性和一致性。当一个线程修改了volatile变量,其他线程读取该变量时,会看到最新的值。

public class VolatileDemo {
    volatile boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public void checkFlag() {
        while (!flag) {
            // 等待flag变为true
        }
        System.out.println("Flag is true");
    }

    public static void main(String[] args) {
        VolatileDemo demo = new VolatileDemo();
        Thread t1 = new Thread(demo::setFlag);
        Thread t2 = new Thread(demo::checkFlag);

        t1.start();
        t2.start();
    }
}

4. Java并发工具类

使用CountDownLatch等待所有任务完成

CountDownLatch用于等待一组线程完成操作。可以设置一个计数器,每个线程完成操作后递减计数器,当计数器到达0时,所有等待的线程被唤醒。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);

        Thread t1 = new Thread(() -> {
            System.out.println("Thread 1 running");
            latch.countDown();
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Thread 2 running");
            latch.countDown();
        });

        Thread t3 = new Thread(() -> {
            System.out.println("Thread 3 running");
            latch.countDown();
        });

        t1.start();
        t2.start();
        t3.start();

        try {
            latch.await(); // 等待所有线程完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All threads completed");
    }
}

使用Semaphore控制并发数

Semaphore用于控制同时访问资源的线程数量。初始化时指定一个整数值,表示允许同时访问的线程数。

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    private static final int MAX_THREAD = 3;
    private static Semaphore semaphore = new Semaphore(MAX_THREAD);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println("Thread " + Thread.currentThread().getId() + " acquired semaphore");
                    Thread.sleep(1000); // 模拟耗时操作
                    semaphore.release(); // 释放许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

使用ThreadPoolExecutor创建线程池

ThreadPoolExecutor是线程池的实现,可以创建一个固定大小的线程池,减少线程的创建和销毁开销。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5); // 创建线程池

        for (int i = 0; i < 10; i++) {
            final int index = i;
            executor.submit(() -> {
                System.out.println("Task " + index + " is running on thread " + Thread.currentThread().getId());
                try {
                    Thread.sleep(1000); // 模拟耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown(); // 关闭线程池
    }
}

5. 并发编程中的常见问题与解决方案

实现避免死锁

死锁是多个线程因相互等待而无法继续执行的状态。常见的死锁原因包括循环等待资源和资源分配不当。解决方法包括避免循环等待和使用超时机制。

public class DeadLockAvoidanceDemo {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1 acquired lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread 1 acquired lock2");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2 acquired lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread 2 acquired lock1");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

活锁和饥饿的概念

活锁是指线程虽然处于运行状态,但由于某些原因无法完成任务。饥饿是指一个线程长时间无法获取资源,导致无法运行。

读写锁和条件变量的使用

读写锁允许一个资源同时被多个读线程共享访问,或者被一个写线程独占访问。条件变量可以用来协调线程间的通信。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReadWriteLockDemo {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean isRead = false;

    public void read() {
        lock.lock();
        try {
            while (isRead) {
                condition.await(); // 等待读取完成
            }
            System.out.println("Reading...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void write() {
        lock.lock();
        try {
            while (!isRead) {
                condition.await(); // 等待读取完成
            }
            System.out.println("Writing...");
            isRead = false;
            condition.signalAll(); // 通知所有等待的线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();

        Thread t1 = new Thread(demo::read);
        Thread t2 = new Thread(demo::read);
        Thread t3 = new Thread(demo::write);

        t1.start();
        t2.start();
        t3.start();
    }
}

6. 实践:构建简单的高并发系统

设计一个简单的多线程程序

设计一个简单的多线程程序,模拟用户访问系统资源的情况。每个线程代表一个用户,访问资源后打印结果。

public class SimpleMultithreadedProgram {
    private static int count = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                synchronized (SimpleMultithreadedProgram.class) {
                    count++;
                    System.out.println("User " + Thread.currentThread().getId() + " accessed resource " + count);
                }
            }).start();
        }
    }
}

使用线程池处理大量请求

使用线程池处理大量请求,可以减少线程的创建和销毁开销,提高程序的执行效率。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolRequestHandler {
    private static final int POOL_SIZE = 5;
    private static ExecutorService executor = Executors.newFixedThreadPool(POOL_SIZE);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            final int index = i;
            executor.submit(() -> {
                System.out.println("Request " + index + " is processing");
                try {
                    Thread.sleep(1000); // 模拟耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown(); // 关闭线程池
    }
}

实现简单的资源锁机制

实现一个简单的资源锁机制,确保在任何时候只有一个线程可以访问资源。


import java.util.concurrent.locks.ReentrantLock;

public class SimpleResourceLock {
    private final ReentrantLock lock = new ReentrantLock();
    private static int count = 0;

    public void accessResource() {
        lock.lock();
        try {
            count++;
            System.out.println("Resource accessed " + count + " times");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        SimpleResourceLock resource = new SimpleResourceLock();

        for (int i = 0; i < 10; i++) {
            new Thread(resource::accessResource).start();
        }
    }
}
``

通过以上示例代码和介绍,读者可以深入了解Java中的并发编程基础知识,并通过实践掌握并发编程的技能。
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消