高并发多线程项目
WEB项目网站服务器开发
多线程
1.1.相关概念
进程:程序就是一个进程;进程是资源分配(计算机资源 CPU 内存 网络。。。) 的基本单位
线程:每个进程中至少要包含一个线程的,也可以包含很多线程;线程才是真正干活,线程是资源调度的基本单位;
OS调度器 OS操作系统调度器
并发:同时发生;大量请求去同时访问服务器,服务器支持并发;
并行:同时进行;任务同时进行;多核CPU 才可以
串行:任务一个接着一个的执行;
同步:一个任务的开始 必须等待上一个任务的结束;
异步:一个任务的开始无需等待其他任务;
1.2.线程的使用
1.2.1 创建线程的方式
1,自定义类型去继承Thread
/** * 自定义类型去继承Thread */ private static void method1() { System.out.println("Main Start"); //对象生成了 Thread customThread = new CustomThread(); //线程的启动;只是通知OS调度器准备好了 准备好可以被调度了 //start方法 排对等着被调用 customThread.start(); // // customThread.run(); System.out.println("Main End "); }
2,实现接口Runnable,没有返回值
/** * 实现接口Runnable,没有返回值 */ private static void method2() { System.out.println("Main Start"); //匿名内部类 Thread threadA = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("ThreadA " + i); } } }, "ThreadA"); //lambda表达式 Thread threadB = new Thread(() -> { for (int i = 100; i < 200; i++) { System.out.println("ThreadB " + i); } }, "ThreadB"); threadA.start(); threadB.start(); System.out.println("Main End"); }
3,实现接口Callable ,有返回值
/** * 实现接口Callable ,有返回值 */ private static void method3() { FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() { //有返回值的任务 @Override public String call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println(i); } //线程休眠 Thread.sleep(10000); return "hello"; } }); Thread threadA = new Thread(futureTask); threadA.start(); String s = null; try { //获取 线程的返回值 get方法是阻塞式方法 必须等待线程执行完毕 才能得到结果 s = futureTask.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("返回值: " + s); }
4,线程池
1.2.2.线程方法
1,常用方法
/** * 常用方法 */ private static void method4() { Thread threadA = new Thread(() -> { for (int s = 0; s < 10; s++) { //获取的线程A // Thread thread = Thread.currentThread(); System.out.println(s); } //让线程A 休眠 try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadA end"); }); //给线程起个名字 threadA.setName("ThreadA"); //线程默认优先级都是5 范围1-10 ;优先级越高有可能优先被调度; threadA.setPriority(10); //1 启动线程并无意味着执行 只是说准备好了 threadA.start(); //获取线程名字 String name = threadA.getName(); //线程的ID long id = threadA.getId(); System.out.println("A Thread Id: " + id); System.out.println("A Thread name : " + name); //获取当前线程;哪个线程在执行这行代码 谁就是当前线程 Thread mainThread = Thread.currentThread(); System.out.println("main id: " + mainThread.getId()); System.out.println("main name: " + mainThread.getName()); /*try { //这是让主线程的休眠,这行代码哪个线程执行 就是哪个线程休眠 Thread.sleep(1000L); //指定时间单位 的休眠; 这行代码哪个线程执行 就是哪个线程休眠 TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }*/ //threadA加入进来意思是主线程必须等待线程A执行完毕 主线程才能继续执行 try { threadA.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Main End"); }
2,join方法
/** * join 方法 */ private static void method5() { System.out.println("Main Start"); Thread threadA = new Thread(() -> { for (int i = 0; i < 100; i++) { System.out.println("threadA " + i); } System.out.println("ThreadA end"); }, "ThreadA"); Thread threadB = new Thread(() -> { for (int i = 100; i < 200; i++) { System.out.println("threadB " + i); } System.out.println("ThreadB end"); }, "ThreadB"); threadA.start(); threadB.start(); //主线程必须等待 线程A和线程B 执行完了 主线程才继续执行 try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Main End"); }
1.3.线程安全
-
多线程同时访问共享内存的时候,多线程同时对共享数据进行操作,造成的数据不一致的情况;
1.3.1. JMM
JMM:Java memory model Java内存模型 规范
JVM内存划分:栈 堆 方法区
JMM内存分布:共享内存(主存: 堆 方法区 ) 线程工作内存(栈 线程的栈,相互独立的 互不影响,局部变量都是工作内存中)
1.3.2. synchronized关键字
-
原子性:一系列操作要么全做 要么全部不做 synchronized具备原子性;
-
synchronized:底层的支持 每个对象其实都有一把对象锁 唯一的 某个对象的对象锁只有一把因此具有唯一性;
-
synchronized用在方法声明处表示线程想要执行这个方法 必须要先获取当前对象的对象锁才能进入方法, 当方法执行完毕 对象锁释放 其他线程再去抢
-
synchronized也可以出现在方法体中,那么线程可以直接进入方法,但是当执行到同步代码块的时候需要先获取对象锁才能执行,当同步代码块执行完毕 对象锁释放;其实是相当于锁的粒度减小了
-
synchronized 内置锁,排他锁(互斥锁), 可重入锁(一旦拿到锁了直接进入下面的同步方法 ),非公平锁,悲观锁
-
synchronized 用在静态方法的声明处 表示要获取的是类对象的对象锁
-
synchronized的使用 如果方法执行过程中出现异常,对象锁会自动释放
1,银行账户的存取和取钱
public void get(double money) { //小括号中需要的是对象;也就是要获取的 是哪个对象的对象锁 synchronized (this) { //1,先从主存拷贝数据 //2,操作 //3,刷新回主存 balance -= money; } } public void save(double money) { synchronized (this) { balance += money; } } /* public synchronized void get(double money) { balance -= money; } public synchronized void save(double money) { balance += money; }*/
2,可重入性
-
同一个线程可直接进入需持有同一个对象锁的代码块
/** * 可重入 */ public synchronized void method1() { System.out.println("method1执行了"); method2(); } public synchronized void method2() { System.out.println("method2执行了"); }
3,静态方法
/** * synchronized 用在静态方法的声明处 表示要获取的是类对象的对象锁 */ public synchronized static void staticMethod() { System.out.println("静态方法"); } public static void staticMethod2() { // 表示要获取的是类对象的对象锁 synchronized (BankAccount.class) { System.out.println("============="); } }
1.3.2.显示锁
-
Java语言实现的 ReentrantLock,也是一把可重入锁
-
锁的申请和释放 都是程序员显示写代码
public class BankAccount2 { private double balance; private ReentrantLock lock = new ReentrantLock(); public BankAccount2() { } public BankAccount2(double balance) { this.balance = balance; } /** * 可重入性 */ public void method1() { lock.lock(); try { System.out.println("method同步代码块的执行 "); lock.lock(); try { System.out.println("可重入"); } finally { lock.unlock(); } } finally { lock.unlock(); } } public void get(double money) { lock.lock(); try { balance -= money; } finally { lock.unlock(); } } public void save(double money) { lock.lock(); try { balance += money; } finally { lock.unlock(); } } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } }
1.4.死锁
/** * 死锁 */ private static void method3() { ReentrantLock lock1 = new ReentrantLock(); ReentrantLock lock2 = new ReentrantLock(); Thread threadA = new Thread(() -> { lock1.lock(); try { System.out.println("线程A获取了lock1的锁 "); try { TimeUnit.MILLISECONDS.sleep(10L); } catch (InterruptedException e) { e.printStackTrace(); } // lock2.lock(); //tryLock如果在指定时间之内获取锁成功返回值是true 如果失败返回值false if (lock2.tryLock(10, TimeUnit.SECONDS)) { try { System.out.println("线程A获取了lock2的锁"); } finally { lock2.unlock(); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock1.unlock(); } }, "ThreadA"); Thread threadB = new Thread(() -> { lock2.lock(); try { System.out.println("线程B获取了lock2的锁 "); try { TimeUnit.MILLISECONDS.sleep(10L); } catch (InterruptedException e) { e.printStackTrace(); } lock1.lock(); try { System.out.println("线程B获取了lock1的锁"); } finally { lock1.unlock(); } } finally { lock2.unlock(); } }, "ThreadB"); threadA.start(); threadB.start(); } private static void method2() { Object o1 = new Object(); Object o2 = new Object(); Thread threadA = new Thread(() -> { synchronized (o1) { System.out.println("线程A获取了o1的对象锁 "); try { TimeUnit.MILLISECONDS.sleep(10L); } catch (InterruptedException e) { e.printStackTrace(); } // synchronized (o2) { System.out.println("线程A获取了o2的对象锁"); } } }, "ThreadA"); Thread threadB = new Thread(() -> { synchronized (o1) { System.out.println("线程B获取了o1的对象锁 "); try { TimeUnit.MILLISECONDS.sleep(10L); } catch (InterruptedException e) { e.printStackTrace(); } // synchronized (o2) { System.out.println("线程B获取了o2的对象锁"); } } }, "ThreadB"); threadA.start(); threadB.start(); }
1.5.集合类
1,List类型
/** * ArrayList是线程不安全的,但是可以外部同步 */ private static void method4() { //线程安全的类:多线程同时操作此类型的对象 数据安全,如果数据不一致就是线程不安全 ArrayList<Integer> arrayList = new ArrayList<>(); Vector<Integer> vector = new Vector<>(); Object o = new Object(); Thread threadA = new Thread(() -> { synchronized (o) { for (int i = 0; i < 100; i++) { arrayList.add(i); } } }); Thread threadB = new Thread(() -> { synchronized (o) { for (int i = 100; i < 200; i++) { arrayList.add(i); } } }); threadA.start(); threadB.start(); try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("size: " + arrayList.size()); }
2,Vector是线程安全的
/** * ArrayList是线程不安全的 ,Vector是线程安全的 */ private static void method5() { Vector<Integer> vector = new Vector<>(); Thread threadA = new Thread(() -> { for (int i = 0; i < 100; i++) { vector.add(i); } }); Thread threadB = new Thread(() -> { for (int i = 100; i < 200; i++) { vector.add(i); } }); threadA.start(); threadB.start(); try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("size: " + vector.size()); }
3,Map类型
/** * 不可变的类型是 最安全 * Hashtable性能比较低 相当于直接锁住整个哈希表 * ConcurrentHashMap性能较高,相当于只锁住某个桶位(某个索引位置) */ private static void method6() { // HashMap<Integer, String> hashMap = new HashMap<>(); ConcurrentHashMap<Integer, String> hashMap = new ConcurrentHashMap<>(); // Hashtable<Integer, String> hashMap = new Hashtable<>(); Thread threadA = new Thread(() -> { for (int i = 0; i < 100; i++) { hashMap.put(i, "java"); } }); Thread threadB = new Thread(() -> { for (int i = 100; i < 200; i++) { hashMap.put(i, "java"); } }); threadA.start(); threadB.start(); try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("size: " + hashMap.size()); }
1.6. CAS自旋锁
-
CAS是计算机指令 CAS(compare and swap 比较并交互)
-
不涉及到锁,也没有什么锁的申请和释放(申请和释放是要占用性能的)
-
但是当并发量比较高的时候 有肯能会浪费CPU
-
CAS自旋锁也是一种 乐观锁
static AtomicInteger atomicInteger = new AtomicInteger(0); static LongAdder longAdder = new LongAdder(); static int num = 0; public static void main(String[] args) { long start = System.currentTimeMillis(); Thread threadA = new Thread(() -> { for (int i = 0; i < 10000000; i++) { longAdder.increment(); //atomicInteger.incrementAndGet(); } }); Thread threadB = new Thread(() -> { for (int i = 0; i < 10000000; i++) { longAdder.decrement(); // atomicInteger.decrementAndGet(); } }); threadA.start(); threadB.start(); try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("num: " + atomicInteger); System.out.println("last: " + (end - start)); }
1.7.线程通信
-
共同访问共享主存;信息交流;
-
生产者消费者模式;
1,synchronized实现
public class Store { //商品数量 private int count; private final static int MAX = 100; /** * 生产者线程 */ public synchronized void put() { //判断是否达到最大值 while (count == MAX) { //sleep方法会不会释放锁;抱着锁睡 // TimeUnit.SECONDS.sleep(1L); // wait方法会释放对象锁,进入对象锁的等候池中 try { System.out.println("仓库满了 生产者开始休息"); //wait方法必须在同步方法 或者是同步代码块中 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; // 唤醒等候池中的所有线程 // notify notifyAll 方法必须在同步方法 或者是同步代码块中 this.notifyAll(); System.out.println(Thread.currentThread().getName() + "生产者生产了一件商品 " + count); } /** * 消费者线程 */ public synchronized void get() { while (count == 0) { try { System.out.println("仓库空了 消费者开始休息"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //消费者消费了一件商品 count--; //唤醒当前对象的对象锁的等候池 //随机唤醒某一个线程 // this.notify(); // 唤醒所有的线程 this.notifyAll(); System.out.println(Thread.currentThread().getName() + "消费者消费了一件商品 " + count); } }
2,ReentrantLock实现
public class Store2 { //商品数量 private int count; private final static int MAX = 100; private ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); /** * 生产者线程 */ public void put() { lock.lock(); try { //判断是否达到最大值 while (count == MAX) { System.out.println("仓库满了 开始等待"); //await signalAll也都是在同步代码块中出现 condition.await(); } count++; condition.signalAll(); System.out.println(Thread.currentThread().getName() + "生产者生产了一件商品 " + count); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /** * 消费者线程 */ public synchronized void get() { lock.lock(); try { while (count == 0) { System.out.println("仓库空了 开始等待"); condition.await(); } count--; condition.signalAll(); System.out.println(Thread.currentThread().getName() + "消费者消费了一件商品 " + count); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
测试代码
Store store = new Store(); for (int i = 0; i < 20; i++) { new Thread(() -> { store.put(); }).start(); } for (int i = 0; i < 20; i++) { new Thread(() -> { store.get(); }).start(); } }
sleep方法和wait方法的区别
1,sleep方法是Thread的静态方法,wait的Object中的方法
2,sleep方法在任意地方都能调用,wait方法只能出现在同步代码块中(同步方法)
3,sleep方法不释放锁 抱着锁睡; wait方法释放锁的
1.8.线程池
-
线程 的频繁创建和销毁消耗资源,为了提升性能,线程池中的线程完成任务之后 继续回到池子中接新的任务,不会销毁;只有当线程池销毁的时候 才会销毁池中线程;重复使用线程
1,工具方法提供的线程池创建方式
private static void method3() { //newFixedThreadPool 创建一个线程个数固定的线程池;当池子中线程都在执行任务也就是说没有空闲线程的时候 //新任务会先进入队列 //栈 后进先出 队列先进先出 ExecutorService executorService = Executors.newFixedThreadPool(2); //runnable executorService.submit(() -> { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }); executorService.submit(() -> { for (int i = 10; i < 20; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }); //Callable Future<String> future = executorService.submit(() -> { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + "hello " + i); } return "java"; }); //获取线程执行完后的结果值,阻塞式的方法 try { String s = future.get(); System.out.println("线程的返回值" + s); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } //关闭线程池;在所有任务完成之后 再销毁线程 再关闭池子 executorService.shutdown(); }
private static void method4() { //只有有任务进来 如果没用空闲线程直接创建新线程接任务,当池子中线程超过60秒没有接新任务的时候 这个线程直接销毁 ExecutorService executorService = Executors.newCachedThreadPool(); executorService.submit(() -> { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }); executorService.shutdown(); }
private static void method6() { //只有一个线程,适合做串行化任务 ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(() -> { System.out.println(Thread.currentThread().getName() + "任务执行了"); }); executorService.submit(() -> { System.out.println(Thread.currentThread().getName() + "任务执行了"); }); } private static void method5() { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2); //定时任务 第一次任务的执行延迟指定时间 后续任务每隔一段时间执行一次 /* scheduledExecutorService.scheduleAtFixedRate(() -> { System.out.println("任务执行了"); }, 1, 3, TimeUnit.SECONDS);*/ //延迟多久 执行任务 任务只执行一次 scheduledExecutorService.schedule(() -> { System.out.println("任务执行了"); }, 5, TimeUnit.SECONDS); }
2,使用ThreadPoolExecutor创建线程池
public static void main(String[] args) { /** * 1 核心线程个数 * 2 最大线程个数 * 3 4 空闲线程的存活时间 * 5 任务队列 长度为3表示只能放3个任务 * * 当有新任务进来 首先看池子中核心线程是否有空闲的 如果有空闲的核心线程 直接交给核心线程去做; * 如果没有此时把任务先进入任务队列中,如果任务队列满了 * 此时看是否达到最大线程 没有达到直接创建非核心线程去执行任务,如果已经达到最大线程个数 此时直接拒绝任务 * * 当非核心线程超过指定时间没有接新任务 直接销毁非核心线程; */ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 3, 6, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(3)); }
1.9.多线程三个核心概念
1,原子性
多个操作要么全部执行 要么全部不执行;
synchronized ,ReentrantLock可以做到原子性
2,可见性
一个线程对共享内存的修改 其他线程可见
synchronized,ReentrantLock 可以做到可见性
volatile可以做到可见性,修饰成员变量,线程每次访问这种变量的时候都必须从主存去获取最新值,而且修改后必须刷新回主存
3,顺序性
编码顺序和执行顺序,JVM为了提升执行性能 有时候会进行指令重排序;
volatile 可以禁止指令重排序;
1.10.单例模式
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
Servlet
Controller
管理器类型 控制器类型 他们的对象一般来说 都是只有一个
1,饿汉单例
/** * 饿汉单例: * *****缺点: 类加载的时候已经创建好对象了;提前浪费了内存; * *****优点: 线程安全的; * 懒汉单例 * ***** */ public class HungrySingleton { //2 静态的成员变量 private static HungrySingleton instance = new HungrySingleton(); //1 构造器私有化 private HungrySingleton() { } //3 共有的静态方法 public static HungrySingleton getInstance() { return instance; } }
2,懒汉单例
/** * 懒汉单例; * 优点: 内存合理使用 * 缺点:线程不安全 */ public class LazySingleton implements Serializable { //禁止指令重排序的 private volatile static LazySingleton instance; private LazySingleton() { } /** * @return */ public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { //双重检测 double check if (instance == null) { //1, 开辟空间 //2, 初始化对象 //3, 给instance赋值 123 132 instance = new LazySingleton(); } } } return instance; } }
1.11.线程状态
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called {@code Object.wait()} * on an object is waiting for another thread to call * {@code Object.notify()} or {@code Object.notifyAll()} on * that object. A thread that has called {@code Thread.join()} * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }