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

JAVA高并发入门详解:从零开始学并发编程

标签:
Java

本文介绍了Java高并发入门的相关知识,包括线程和进程的基本概念、Java线程模型、并发编程的基本概念以及线程的创建和管理。文章还详细讲解了线程同步与互斥、并发工具类的应用,以及如何避免并发编程中的常见问题。

1. Java并发编程基础

理解线程与进程

在计算机系统中,进程和线程是两个核心概念,它们共同构成了操作系统资源管理和并发执行的基础。

进程是指一个程序的执行实例,它是操作系统中的基本单位。每个进程拥有独立的地址空间,包含代码段、数据段、堆、栈等。

线程是进程内的一个执行单元,共享进程的资源,但有自己的栈和局部变量。一个进程中可以有多个线程并发运行,以达到更好的性能和资源利用率。

Java线程模型介绍

Java中的线程模型基于操作系统的线程模型。Java虚拟机(JVM)是线程的运行环境,而每个线程对应着一个运行中的Java方法。Java线程模型可以分为几个层次:

  1. 操作系统层:操作系统提供线程调度和资源管理功能。
  2. JVM层:Java虚拟机负责线程的创建和销毁。
  3. Java API层:Java提供了丰富的API来管理线程,如Thread类和Runnable接口。

下面是一个简单的Java线程示例,使用Thread类创建并启动一个线程。

public class SimpleThreadExample {
    public static void main(String[] args) {
        // 创建一个新的线程
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("线程正在运行...");
            }
        };

        // 启动线程
        thread.start();
    }
}

Java并发编程的基本概念

Java并发编程涉及多个线程同时访问共享资源,为了保证程序的正确性,需要了解一些基本概念:

  • 线程安全:线程安全是指在多个线程访问同一个资源时,能够保证资源的使用不会出错。
  • 锁定:锁定机制是用来确保在任意时刻只有一个线程能访问共享资源。
  • 死锁:两个或多个线程互相等待对方释放资源,导致所有线程都无法继续执行。
  • 竞态条件:多个线程同时修改同一资源,导致结果不确定。
  • 线程同步:确保多个线程在访问共享资源时,按照预定的顺序执行。
2. 创建和管理线程

使用Thread类创建线程

使用Thread类是最直接的创建线程的方式。通过继承Thread类并重写run方法,可以创建一个新线程。

public class ThreadExample extends Thread {
    public void run() {
        System.out.println("我在一个新的线程中运行");
    }

    public static void main(String[] args) {
        ThreadExample example = new ThreadExample();
        example.start();
    }
}

使用Runnable和Callable接口

Runnable接口和Callable接口提供了另一种创建线程的方法。它们不像Thread类那样直接表示一个线程,而是提供一个线程执行的方法定义。

public class RunnableExample implements Runnable {
    public void run() {
        System.out.println("我在一个Runnable线程中运行");
    }

    public static void main(String[] args) {
        RunnableExample example = new RunnableExample();
        Thread thread = new Thread(example);
        thread.start();
    }
}

public class CallableExample implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "我在一个Callable线程中运行";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableExample example = new CallableExample();
        FutureTask<String> futureTask = new FutureTask<>(example);
        Thread thread = new Thread(futureTask);
        thread.start();
        String result = futureTask.get();
        System.out.println(result);
    }
}

线程的生命周期和状态

线程在生命周期中会经历几个状态:

  • New:线程已经被创建,但还没有调用start方法。
  • Runnable:线程已经被创建,并且正在执行run方法。
  • Blocked:等待获取锁。
  • Waiting:等待其他线程的特定动作。
  • Timed-Waiting:等待指定时间后唤醒。
  • Terminated:线程执行完毕。

下面是一个具体的Java代码示例,展示如何创建和管理线程状态。

public class ThreadLifeCycle {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000); // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        System.out.println("线程状态: " + thread.getState()); // 输出:NEW
        thread.start();
        Thread.sleep(500); // 暂停主线程
        System.out.println("线程状态: " + thread.getState()); // 输出:RUNNABLE

        thread.interrupt(); // 打断线程
        Thread.sleep(1000); // 暂停主线程
        System.out.println("线程状态: " + thread.getState()); // 输出:TERMINATED
    }
}
3. 线程同步与互斥

synchronized关键字详解

synchronized关键字用于创建同步锁,保证同一时间只有一个线程能够访问共享资源。

public class SynchronizedExample {
    private int count = 0;

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

    public synchronized int getCount() {
        return count;
    }

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

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终计数: " + example.getCount()); // 输出:2000
    }
}

互斥锁与读写锁的应用

互斥锁通常通过synchronized关键字实现,而读写锁则提供了更细粒度的控制,适用于读多写少的场景。

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
    private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock rlock = rwlock.readLock();
    private final ReentrantReadWriteLock.WriteLock wlock = rwlock.writeLock();
    private int count = 0;

    public void read() {
        rlock.lock();
        try {
            System.out.println("读取计数: " + count);
        } finally {
            rlock.unlock();
        }
    }

    public void write() {
        wlock.lock();
        try {
            count++;
            System.out.println("写入计数: " + count);
        } finally {
            wlock.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockExample example = new ReadWriteLockExample();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.read();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.write();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

volatile关键字的作用

volatile关键字保证了变量的可见性和有序性,但不保证原子性。

public class VolatileExample {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

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

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终计数: " + example.count); // 输出:2000
    }
}
4. 并发工具类介绍

CountDownLatch和CyclicBarrier

CountDownLatchCyclicBarrier都是用于线程同步的工具类,前者用于等待全部线程完成,后者用于等待所有线程到达某个屏障点。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

public class SyncToolsExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2);

        Thread thread1 = new Thread(() -> {
            System.out.println("线程1开始");
            try {
                Thread.sleep(1000);
                System.out.println("线程1结束");
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("线程2开始");
            try {
                cyclicBarrier.await();
                System.out.println("所有线程都到达屏障点");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();

        countDownLatch.await(); // 等待线程1完成

        try {
            cyclicBarrier.await(); // 等待线程2到达屏障点
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("所有线程已完成");
    }
}

Semaphore和Exchanger

Semaphore用于控制同时访问特定资源的线程数量,而Exchanger用于两个线程之间的数据交换。

import java.util.concurrent.Semaphore;
import java.util.concurrent.Exchanger;

public class SemaphoreExchangerExample {
    private static final Semaphore semaphore = new Semaphore(2);
    private static final Exchanger<String> exchanger = new Exchanger<>();

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println("线程1获取到许可");
                String result = exchanger.exchange("Hello");
                System.out.println("线程1交换结果: " + result);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println("线程2获取到许可");
                String result = exchanger.exchange("World");
                System.out.println("线程2交换结果: " + result);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
    }
}

并发集合类(ConcurrentHashMap, CopyOnWriteArrayList等)

ConcurrentHashMapCopyOnWriteArrayList是并发集合类,提供了线程安全的集合操作。

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class ConcurrentCollectionsExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        Thread thread1 = new Thread(() -> {
            map.put("key1", "value1");
            list.add("value1");
            System.out.println("线程1添加完成");
        });

        Thread thread2 = new Thread(() -> {
            map.put("key2", "value2");
            list.add("value2");
            System.out.println("线程2添加完成");
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("map: " + map);
        System.out.println("list: " + list);
    }
}
5. 并发编程中的常见问题

死锁的现象及避免方法

死锁是一种严重的并发问题,可以通过设计合理的锁顺序来避免。

public class DeadlockExample {
    public static void main(String[] args) {
        Resource r1 = new Resource();
        Resource r2 = new Resource();

        Thread thread1 = new Thread(() -> {
            synchronized (r1) {
                synchronized (r2) {
                    System.out.println("线程1获取两个锁");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (r2) {
                synchronized (r1) {
                    System.out.println("线程2获取两个锁");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

class Resource {
    // 资源类
}

活锁与饥饿

活锁是指一个线程不断尝试执行任务但无法完成,而饥饿是指某些线程长时间无法获得执行机会。

下面是一个具体的Java代码示例,展示如何避免活锁和饥饿。

public class StarvationExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            while (true) {
                synchronized (StarvationExample.class) {
                    System.out.println("线程1运行");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (StarvationExample.class) {
                System.out.println("线程2运行");
            }
        });

        thread1.start();
        thread2.start();
    }
}

避免竞态条件的方法

竞态条件是指多个线程同时修改共享资源导致结果不确定的问题。可以通过使用synchronized关键字或并发集合类来避免。

public class RaceConditionExample {
    private static int count = 0;

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

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

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                increment();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终计数: " + count); // 输出:2000
    }
}
6. 实战案例:简单的银行账户转账项目

使用多线程模拟多个用户同时进行转账操作

下面是一个简单的银行账户转账项目,模拟多个用户同时进行转账操作,并保证账户余额的正确性。

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class BankAccount {
    private static final ConcurrentHashMap<Integer, BankAccount> accounts = new ConcurrentHashMap<>();
    private final int id;
    private AtomicInteger balance;

    public BankAccount(int id, int initialBalance) {
        this.id = id;
        this.balance = new AtomicInteger(initialBalance);
        accounts.put(id, this);
    }

    public void deposit(int amount) {
        balance.addAndGet(amount);
    }

    public void withdraw(int amount) {
        balance.addAndGet(-amount);
    }

    public int getBalance() {
        return balance.get();
    }

    public static void main(String[] args) {
        BankAccount account1 = new BankAccount(1, 1000);
        BankAccount account2 = new BankAccount(2, 2000);

        Thread thread1 = new Thread(() -> {
            account1.withdraw(500);
            account2.deposit(500);
            System.out.println("账户1余额: " + account1.getBalance());
            System.out.println("账户2余额: " + account2.getBalance());
        });

        Thread thread2 = new Thread(() -> {
            account2.withdraw(500);
            account1.deposit(500);
            System.out.println("账户1余额: " + account1.getBalance());
            System.out.println("账户2余额: " + account2.getBalance());
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("账户1最终余额: " + account1.getBalance());
        System.out.println("账户2最终余额: " + account2.getBalance());
    }
}

保证账户余额正确性的方法

为了确保账户余额的正确性,可以使用AtomicInteger等原子类,或者使用synchronized关键字或并发集合类来保证线程安全。

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

public class BankAccountWithLock {
    private final int id;
    private int balance;
    private final Lock lock = new ReentrantLock();

    public BankAccountWithLock(int id, int initialBalance) {
        this.id = id;
        this.balance = initialBalance;
    }

    public void deposit(int amount) {
        lock.lock();
        try {
            balance += amount;
        } finally {
            lock.unlock();
        }
    }

    public void withdraw(int amount) {
        lock.lock();
        try {
            balance -= amount;
        } finally {
            lock.unlock();
        }
    }

    public int getBalance() {
        return balance;
    }

    public static void main(String[] args) {
        BankAccountWithLock account1 = new BankAccountWithLock(1, 1000);
        BankAccountWithLock account2 = new BankAccountWithLock(2, 2000);

        Thread thread1 = new Thread(() -> {
            account1.withdraw(500);
            account2.deposit(500);
            System.out.println("账户1余额: " + account1.getBalance());
            System.out.println("账户2余额: " + account2.getBalance());
        });

        Thread thread2 = new Thread(() -> {
            account2.withdraw(500);
            account1.deposit(500);
            System.out.println("账户1余额: " + account1.getBalance());
            System.out.println("账户2余额: " + account2.getBalance());
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("账户1最终余额: " + account1.getBalance());
        System.out.println("账户2最终余额: " + account2.getBalance());
    }
}
点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消