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

Java高并发入门教程

标签:
Java
概述

本文介绍了Java高并发入门的相关知识,涵盖了高并发的基础概念、对Java应用的影响、常用并发工具以及线程同步与互斥机制。文章还详细讲解了并发控制机制、并发编程实践、常见并发问题与解决方案,以及Java高并发性能调优的方法。java高并发入门内容丰富,旨在帮助开发者理解和应用Java高并发技术。

Java高并发基础概念

什么是高并发

高并发是指在短时间内,大量的用户请求同时到达服务器,服务器需要能够高效、稳定地处理这些请求。在互联网应用中,高并发是一个非常重要的性能指标,尤其是在电商平台、社交平台等需要处理大量用户请求的应用场景下。

高并发主要体现在以下两个方面:

  1. 高并发请求:大量的用户同时发起请求,如电商平台的秒杀活动,需要在极短的时间内处理大量的购买请求。
  2. 高并发响应:服务器需要在短时间内返回大量的响应,确保用户体验。

高并发对Java应用的影响

高并发对Java应用的影响主要体现在以下几个方面:

  1. 系统资源消耗:大量的并发请求会导致系统资源消耗增加,如CPU、内存等。
  2. 响应时间:如果处理不当,高并发会延长响应时间,影响用户体验。
  3. 系统稳定性:高并发场景下,系统的稳定性会受到严峻考验,容易出现死锁、资源竞争等问题。
  4. 性能优化:高并发要求系统具备高效的性能优化策略,如负载均衡、缓存机制等。

Java中常用的并发工具

Java提供了丰富的并发工具,帮助开发者更好地处理高并发场景。以下是一些常用的并发工具:

  1. synchronized:用于实现同步机制,防止多个线程同时访问共享资源。
  2. volatile:用于保证变量的可见性,确保一个线程对变量的修改能够被其他线程看到。
  3. 并发容器:如ConcurrentHashMapCopyOnWriteArrayList等,提供线程安全的数据结构。
  4. 并发工具类:如CountDownLatchCyclicBarrierSemaphore等,用于实现复杂的并发控制逻辑。

Semaphore 示例代码

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    static Semaphore semaphore = new Semaphore(2);

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println("Thread " + Thread.currentThread().getName() + " is running");
                    Thread.sleep(1000);
                    System.out.println("Thread " + Thread.currentThread().getName() + " is done");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}
Java线程基础

Java线程的创建与启动

在Java中,可以通过继承Thread类或实现Runnable接口来创建线程。下面分别给出两个示例。

继承Thread类

public class MyThread extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

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

实现Runnable接口

public class MyRunnable implements Runnable {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

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

线程同步与互斥

在多线程环境中,多个线程可能会同时访问共享资源,这样会导致资源竞争和数据不一致。线程同步和互斥机制可以解决这些问题。

使用synchronized关键字

public class Counter {
    private int count = 0;

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

    public synchronized int getCount() {
        return count;
    }
}

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

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

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

        System.out.println("Final count: " + counter.getCount());
    }
}

线程通信机制

Java提供了多种线程通信机制,如waitnotifynotifyAll等,用于线程之间的同步和协作。

使用wait和notify

public class WaitNotifyExample {
    private static Object lock = new Object();
    private static boolean ready = false;

    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Producer is working...");
                ready = true;
                lock.notify();
            }
        });

        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                while (!ready) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Consumer is consuming...");
            }
        });

        consumer.start();
        producer.start();
    }
}

使用wait和notifyAll

public class WaitAllExample {
    private static Object lock = new Object();
    private static boolean ready = false;

    public static void main(String[] args) {
        Thread producer1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Producer 1 is working...");
                ready = true;
                lock.notifyAll();
            }
        });

        Thread producer2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Producer 2 is working...");
                ready = true;
                lock.notifyAll();
            }
        });

        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                while (!ready) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Consumer is consuming...");
            }
        });

        consumer.start();
        producer1.start();
        producer2.start();
    }
}
Java并发控制机制

锁机制(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 t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

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

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

        System.out.println("Final count: " + example.getCount());
    }
}

ReentrantLock 示例代码

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private int count = 0;
    private final 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();
        }
    }

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

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

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

        System.out.println("Final count: " + example.getCount());
    }
}

volatile关键字的作用

volatile关键字用于保证变量的可见性,确保一个线程对变量的修改能够被其他线程看到。

示例代码

public class VolatileExample {
    private volatile boolean flag = false;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public boolean getFlag() {
        return flag;
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileExample example = new VolatileExample();
        Thread t1 = new Thread(() -> {
            example.setFlag(true);
        });

        Thread t2 = new Thread(() -> {
            while (!example.getFlag()) {
                // 等待flag变为true
            }
            System.out.println("Flag is true");
        });

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

Java并发工具类(CountDownLatch、CyclicBarrier)

Java并发工具类提供了更高级的并发控制机制。

CountDownLatch

CountDownLatch用于等待一组事件完成,然后继续执行。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(2);

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

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

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

        latch.await();

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

CyclicBarrier

CyclicBarrier用于实现一组线程等待所有线程到达某个屏障点后,再继续执行。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        CyclicBarrier barrier = new CyclicBarrier(2);

        Thread t1 = new Thread(() -> {
            System.out.println("Thread 1 is working...");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Thread 2 is working...");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        });

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

        t1.join();
        t2.join();

        System.out.println("All threads completed");
    }
}
并发编程实践

实战多线程编程

多线程编程是Java并发编程的核心,下面通过一个简单的例子来展示如何实现多线程编程。

示例代码

public class MultiThreadExample {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("Thread 1 " + i);
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("Thread 2 " + i);
            }
        });

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

并发容器的使用

Java提供了多种并发容器,如ConcurrentHashMapCopyOnWriteArrayList等。

示例代码

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

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

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                map.put("key" + i, i);
                list.add("value" + i);
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(map.get("key" + i));
                System.out.println(list.get(i));
            }
        });

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

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

防止死锁的最佳实践

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉,则这些线程将永远无法继续执行。

避免死锁的策略

  1. 避免嵌套锁:尽量避免在一个线程中获取多个锁。
  2. 按顺序获取锁:确保每个线程总是按相同的顺序获取锁。
  3. 超时机制:在获取锁时设置超时时间,超过时间后释放锁。

示例代码

import java.util.concurrent.TimeUnit;

public class DeadlockAvoidanceExample {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
            bower.bowBack(this);
        }

        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");

        new Thread(() -> {
            alphonse.bow(gaston);
        }).start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            gaston.bow(alphonse);
        }).start();
    }
}

避免死锁的策略与示例

  1. 使用超时机制防止死锁
  2. 在获取锁时设置超时时间,超过时间后释放锁

示例代码

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

public class DeadlockAvoidanceExample {
    static class Friend {
        private final String name;
        private final ReentrantLock lock;

        public Friend(String name) {
            this.name = name;
            this.lock = new ReentrantLock();
        }

        public String getName() {
            return this.name;
        }

        public void bow(Friend bower) {
            lock.lock();
            try {
                bower.lock.lock();
            } catch (Exception e) {
                bower.lock.unlock();
            } finally {
                lock.unlock();
            }
            System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");

        new Thread(() -> {
            alphonse.bow(gaston);
        }).start();

        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            gaston.bow(alphonse);
        }).start();
    }
}
常见并发问题与解决方案

并发下的内存泄漏

内存泄漏是指程序在运行过程中,内存空间被分配后,未能及时释放,导致程序占用的内存空间不断增加,最终耗尽内存资源。

导致内存泄漏的原因

  1. 静态集合类实例:静态集合类实例会一直占用内存,直到程序结束。
  2. 静态内部类:静态内部类持有外部类对象的引用,导致外部类对象无法被垃圾回收。
  3. 线程泄漏:线程没有被正确关闭,导致内存资源无法释放。

防止内存泄漏的策略

  1. 合理使用静态集合类:避免使用静态集合类实例,或者在不再使用时及时清空集合。
  2. 避免静态内部类:尽量避免使用静态内部类,或者在静态内部类中不要持有外部类对象的引用。
  3. 正确管理线程:确保线程在使用完成后能够被正确关闭,释放占用的资源。

示例代码

public class MemoryLeakExample {
    static class StaticCollection {
        static final java.util.List<String> list = new java.util.ArrayList<>();

        public static void add(String s) {
            list.add(s);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            StaticCollection.add("item" + i);
        }
    }
}

并发下的资源竞争

资源竞争是指多个线程同时访问同一个资源,导致资源被重复修改或访问,产生数据不一致的问题。

导致资源竞争的原因

  1. 未正确使用锁:多个线程未按照正确的顺序获取锁,导致资源竞争。
  2. 未使用并发容器:使用非线程安全的容器,导致数据竞争。

解决资源竞争的方法

  1. 使用锁机制:使用synchronized关键字或ReentrantLock,确保同一时间只有一个线程访问资源。
  2. 使用并发容器:使用ConcurrentHashMapCopyOnWriteArrayList等并发容器,确保线程安全。

示例代码

public class ResourceRaceExample {
    private int counter = 0;

    public void increment() {
        counter++;
    }

    public static void main(String[] args) {
        ResourceRaceExample example = new ResourceRaceExample();

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

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

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

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

        System.out.println("Final count: " + example.counter);
    }
}

并发环境下的线程安全

线程安全是指在多线程环境下,程序能够正确地运行,不会出现数据不一致或资源竞争的问题。

保证线程安全的方法

  1. 使用同步机制:使用synchronized关键字或ReentrantLock,确保同一时间只有一个线程访问共享资源。
  2. 使用原子操作:使用AtomicIntegerAtomicLong等原子类,确保操作的原子性。
  3. 使用并发容器:使用ConcurrentHashMapCopyOnWriteArrayList等并发容器,确保线程安全。

示例代码

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadSafetyExample {
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void increment() {
        counter.incrementAndGet();
    }

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

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

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

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

        System.out.println("Final count: " + counter.get());
    }
}
Java高并发性能调优

JVM参数调优

JVM参数调优是提高Java应用性能的重要手段,可以通过调整JVM参数来优化内存分配、垃圾回收等。

常用的JVM参数

  1. Xmx:设置堆的最大大小,如-Xmx1024m
  2. Xms:设置堆的初始大小,如-Xms512m
  3. Xss:设置线程栈的大小,如-Xss512k
  4. XX:MaxPermSize:设置永久代的最大大小,适用于JDK 8之前的版本。
  5. XX:MetaspaceSize:设置元空间的初始大小,适用于JDK 8及以后的版本。
  6. XX:MaxMetaspaceSize:设置元空间的最大大小,适用于JDK 8及以后的版本。
  7. XX:NewRatio:设置新生代和老年代的比例,如-XX:NewRatio=2
  8. XX:SurvivorRatio:设置新生代中Eden区和Survivor区的比例,如-XX:SurvivorRatio=6
  9. XX:MaxTenuringThreshold:设置对象晋升到老年代的年龄阈值,如-XX:MaxTenuringThreshold=15
  10. XX:UseConcMarkSweepGC:使用CMS垃圾回收器,适用于长时间运行的应用。
  11. XX:UseG1GC:使用G1垃圾回收器,适用于大内存的应用。

示例代码

public class JvmTuningExample {
    public static void main(String[] args) {
        // JVM参数
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024);
        int allocatedMemory = (int) (Runtime.getRuntime().totalMemory() / 1024 / 1024);
        int freeMemory = (int) (Runtime.getRuntime().freeMemory() / 1024 / 1024);

        System.out.println("Max Memory: " + maxMemory + " MB");
        System.out.println("Allocated Memory: " + allocatedMemory + " MB");
        System.out.println("Free Memory: " + freeMemory + " MB");
    }
}

GC对高并发的影响

垃圾回收(GC)对高并发应用的影响主要体现在以下几个方面:

  1. 停顿时间:GC过程中,所有应用线程会被暂停,导致应用响应时间增加。
  2. 资源消耗:频繁的GC会导致CPU和内存资源消耗增加。
  3. 性能优化:合理的GC调优可以减少停顿时间,提高应用性能。

减少GC停顿时间的方法

  1. 减少对象创建:尽量减少不必要的对象创建,减少GC的频率。
  2. 选择合适的GC算法:根据应用的特性选择合适的GC算法,如CMS、G1等。
  3. 优化堆的大小:合理设置堆的大小,减少GC的频率。
  4. 使用并发GC:使用并行GC或并发GC,减少GC对应用的影响。

示例代码

public class GcExample {
    public static void main(String[] args) {
        System.gc(); // 主动触发GC
    }
}

并发性能监控与分析工具

监控和分析并发性能是提高应用性能的重要手段,常用的工具包括JVisualVM、JProfiler、VisualVM等。

常用的监控工具

  1. JVisualVM:JDK自带的监控工具,可以监控应用的CPU、内存、线程等。
  2. JProfiler:一款专业的Java性能分析工具,支持线上和离线分析。
  3. VisualVM:一款开源的Java应用监控工具,支持在线和离线分析。
  4. GCeasy:一款在线的GC日志分析工具,支持多种GC算法的日志分析。

使用JVisualVM监控应用

public class JVisualVMExample {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

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

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

通过以上内容,希望读者能够对Java高并发编程有一个全面的理解,并能够实际应用到项目开发中。并发编程是一项复杂而有趣的技能,通过不断的实践和学习,可以提高Java应用的性能和稳定性。

点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消