热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java多线程与并发(一)

多线程与并发的基础问题并发就是指程序同时处理多个任务的能力(一个程序被多个用户访问都能看到自己预期的结果)并发的根源在于对多任务情况下访问资源的有效控

多线程与并发的基础问题

并发就是指程序同时处理多个任务的能力(一个程序被多个用户访问都能看到自己预期的结果)
并发的根源在于对多任务情况下访问资源的有效控制!


  • 并发背后的问题

public class DownloadSimple {private static int user = 1;//同时模拟的并发用户访问数量//private static int user = 10;private static int dowloadCounts = 5000;//用户的真实下载数private static int count= 0;//计数器public static void main(String[] args) {//调度器,jdk1.5之后引入current对于并发的支持ExecutorService executor = Executors.newCachedThreadPool();//信号量 ,用于模拟并发用户数final Semaphore semaphore = new Semaphore(user);for(int i =0;i//通过多线程模拟多个用户访问的下载次数executor.execute(new Runnable() {@Overridepublic void run() {try{semaphore.acquire();add();semaphore.release();}catch(Exception e){e.printStackTrace();}}});}try {//延迟主线程结束--让for循环中代码执行完毕Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count);}private static void add(){count++;}

user=1单个用户访问
这里写图片描述

user=10多个用户访问的时候:

这里写图片描述

无处不存在并发的问题:
并发控制是关键问题


  • 课程导图
    基本概念
    多线程(Thread)
    线程池(Executor)
    并发容器(Collections)
    同步工具(Tools)
    原子对象(Atomic)

  • 基础概念

程序、进程和线程的区别?
程序是静态的概念,windows下是指.exe程序
进程是动态的概念,是程序在运行状态,进程说明程序在内存中的边界
线程是指进程内的一个“基本任务”,每个线程都有自己的功能,是cpu分配与调度的基本单位。

一个程序有多个进程(之间互不影响)

这里写图片描述

多核CPU是线程并行的
单核CPU是线程并发的(CPU的时间片)

同步和异步:

这里写图片描述

回调(异步)–Ajax
NIO-非阻塞IO

临界区:(eg:办公室中的一台打印机)
临界区表示一种公共资源与共享数据,可以被多个线程使用。
同一时间只能有一个线程访问临界区域(阻塞状态),其它资源必须等待。

这里写图片描述

避免以上锁发生的解决办法:为每一个线程分配一个自己独立的不被其他影响的资源

线程安全:在拥有共享数据的多个线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

线程安全不安全就是区别单用户多用户对公共访问资源的处理是否符合预期处理

线程安全的三大特性:
原子性:即一个操作或者多个操作,要么全部执行且执行的过程不会被其他因素打断,要么就都不执行 i = i +1
可见性:当多个线程访问同一个变量的时候,一个线程修改了这个变量的值,其它线程能够立即看得到修改的值

这里写图片描述
用户1修改之后,没有通知用户2线程

有序性: 如果在本线程内观察,所有的操作都是有序的,如果在一个线程内观察另外一个线程,所有操作都是无序的;

这里写图片描述
JVM自动进行防止重排序,所以应该不会出现重排序,打乱有序性


  • *Java内存模型
    Java Memory Model(在真实物理内存上开辟的空间)

这里写图片描述

堆内存(Heap):用于存储对象,JVM全局唯一,堆是不连续的,执行效率低,所有线程共享
方法区(静态区):类结构信息,静态变量和静态方法信息,存储内容是不变的,存储字符串,在堆中开辟的一块空间

栈内存(Stack):每个线程创建一个栈,存储执行方法的执行信息,线程私有,无法共享。连续存储,执行效率高,先进后出,后进先出

这里写图片描述

直至main函数弹出栈
销毁线程栈,销毁线程


  • Java多线程
    创建多线程的三种方式:
    1.继承Thread类创建线程
    2.实现Runnable接口来创建线程
    3.使用Callable和Future创建线程

//使用继承Thread的方式来实现多线程

public class Methed1{public static void main(String[] args) {new ExtendsThread().start();//创建一个新的线程并启动//setName("wanghaoxin")可以设置线程名称}}
class ExtendsThread extends Thread{public void run(){Integer speed &#61; new Random().nextInt(100);for(int i &#61; 0;i<100 ;i&#43;&#43;){try {//当前线程休眠1sThread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//打印当前线程的名字System.out.println(this.getName()&#43;"---已前进--" &#43;(i*speed)&#43;"---速度----"&#43;speed);}}}

三个并列的线程可能执行顺序不一样

ExtendsThread liuxiang &#61; new ExtendsThread();liuxiang.setName("刘翔");ExtendsThread wanghaoxin &#61; new ExtendsThread();wanghaoxin.setName("王五");ExtendsThread lisan &#61; new ExtendsThread();lisan.setName("路飞");liuxiang.start();//创建一个新的线程并启动wanghaoxin.start();//创建一个新的线程并启动lisan.start();//创建一个新的线程并启动

执行顺序依赖于线程的优先级&#xff0c;可以手动设置

程序一旦程序启动之后有五个线程

这里写图片描述
java本身运行的过程中就是多线程的

垃圾回收线程和main主线程


  • 实现Runnable接口实现线程

在Runnable接口中无法获取this.getName()获取线程名称
只能用:

public Thread(Runnable target) {init(null, target, "Thread-" &#43; nextThreadNum(), 0);}

通过实现Runnable接口来新建线程


public class Methed2 {public static void main(String[] args) {ImplRunnable liuxiang &#61; new ImplRunnable();Thread thread &#61; new Thread(liuxiang);thread.setName("liuxian");Thread thread2 &#61; new Thread(new ImplRunnable());thread2.setName("WANGHAXIN");thread.start();thread2.start();}}
class ImplRunnable implements Runnable{&#64;Overridepublic void run() {Integer speed &#61; new Random().nextInt(100);for(int i &#61; 0;i<100 ;i&#43;&#43;){try {//当前线程休眠1sThread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}//打印当前线程的名字//此处不可以用this.getName(因为不getName属于Thread的方法&#xff0c;Runnable接口中并没有)System.out.println(Thread.currentThread().getName()&#43;"---已前进--" &#43;(i*speed)&#43;"---速度----"&#43;speed);}}}

  • 实现Callable接口–利用线程池
    jdk1.5之后为我们专门提供了一个并发工具包&#xff0c;java.util.concurrent。

java.util.concurrent包含许多线程安全、测试良好、高性能的并发构建块。创建concurrent的目的就是要实现Collection框架对数据结构所执行的并发操作。通过提供一组可靠的&#xff0c;高性能并发构建块&#xff0c;开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性

Callable<Integer> myCallabel&#61; new implCall(); FutureTask<Integer> ft&#61;new FutureTask<Integer>(myCallabel); System.out.println(Thread.currentThread().getName()&#43;" "&#43;i);Thread th&#61; new Thread(ft);th.start();

专门用于并发机制

需要线程的数目已经在创建线程池时已经定义好
liuxiang是实现Callable接口的实例化对象&#xff0c;Future用于接收返回值

implCall liuxiang &#61; new implCall();
Future future &#61; executorService.submit(liuxiang);

代码案例&#xff1a;&#xff08;线程池实现&#xff09;

//对于一个线程而言&#xff0c;Callable有返回值&#xff0c;
public class Methed3 {public static void main(String[] args) {//利用Executors线程调度器来实现 ---Executors是调度器对线程池进行管理ExecutorService executorService &#61; Executors.newFixedThreadPool(3);//创建一个线程池&#xff0c;里边添有三个空线程implCall liuxiang &#61; new implCall();//实例化Callable对象implCall wanghaoxin &#61; new implCall();implCall lufei &#61; new implCall();//Future用于接收线程内部call方法的返回值//将这个对象扔到线程池中&#xff0c;线程池自动分配一个线程来运行liuxiang这个对象的call方法Future future &#61; executorService.submit(liuxiang);Future future2 &#61; executorService.submit(wanghaoxin);Future future3 &#61; executorService.submit(lufei);//此时线程池容量大小是3&#xff0c;再往线程池中添加时会报错try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{executorService.shutdown();//关闭线程池释放所有资源}try {System.out.println("累计跑了" &#43; future.get());System.out.println("累计跑了" &#43; future2.get());System.out.println("累计跑了" &#43; future3.get());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
/*** 实现Callable接口可以允许我们的线程返回值或者抛出异常* &#64;author Administrator**/
class implCall implements Callable{&#64;Overridepublic Object call() throws Exception {Integer speed &#61; new Random().nextInt(100);Integer distince &#61; 0 ; //总共奔跑的距离for(int i &#61; 0;i<100 ;i&#43;&#43;){distince &#61; i*speed;//打印当前线程的名字System.out.println(Thread.currentThread().getName()&#43;"---已前进--" &#43;(i*speed)&#43;"---速度----"&#43;speed);}return distince;}}

这三个线程是彼此没有任何交集&#xff0c;利用线程池手段是开发的主要实现方案–方便对线程池进行有效管理&#xff08;最著名–数据库连接池&#xff0c;数据库中的每一个连接其实就是一个线程&#xff0c;线程池就是连接池的底层实现&#xff09;

线程池更安全—-保证线程安全的工具类

这里写图片描述

1&#xff09;继承Thread&#xff1a;优点&#xff1a;编程简单&#xff0c;执行效率高&#xff0c;但继承&#xff0c;无法对线程组进行有效控制&#xff0c;任何使用场景都不推荐
2&#xff09;实现Runnable接口&#xff0c;面向接口编程&#xff0c;执行效率高&#xff0c;无法对线程组进行有效控制&#xff0c;没有返回值。异常&#xff0c;使用场景&#xff1a;适用于简单的多线程程序
3&#xff09;利用线程池&#xff1a;容器管理线程&#xff0c;允许返回值和异常&#xff0c;执行效率相对低&#xff08;容器本身有调度的时间耗费&#xff0c;线程切换等等&#xff09;&#xff0c;编程麻烦&#xff0c;使用场景&#xff1a;企业级应用&#43;推荐使用。


推荐阅读
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 深入浅析JVM垃圾回收机制与收集器概述
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》的阅读心得进行整理,详细探讨了JVM的垃圾回收机制及其各类收集器的特点与应用场景。通过分析不同垃圾收集器的工作原理和性能表现,帮助读者深入了解JVM内存管理的核心技术,为优化Java应用程序提供实用指导。 ... [详细]
  • 【并发编程】全面解析 Java 内存模型,一篇文章带你彻底掌握
    本文深入解析了 Java 内存模型(JMM),从基础概念到高级特性进行全面讲解,帮助读者彻底掌握 JMM 的核心原理和应用技巧。通过详细分析内存可见性、原子性和有序性等问题,结合实际代码示例,使开发者能够更好地理解和优化多线程并发程序。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • 开发日志:201521044091 《Java编程基础》第11周学习心得与总结
    开发日志:201521044091 《Java编程基础》第11周学习心得与总结 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • JDK 1.8引入了多项并发新特性,显著提升了编程效率。本文重点探讨了LongAdder和StampedLock的特性和应用场景。此外,还介绍了在多线程环境中发生死锁时,如何通过jps命令进行诊断和排查,提供了详细的步骤和示例。这些改进不仅增强了系统的性能,还简化了开发者的调试工作。 ... [详细]
  • 分布式开源任务调度框架 TBSchedule 深度解析与应用实践
    本文深入解析了分布式开源任务调度框架 TBSchedule 的核心原理与应用场景,并通过实际案例详细介绍了其部署与使用方法。首先,从源码下载开始,详细阐述了 TBSchedule 的安装步骤和配置要点。接着,探讨了该框架在大规模分布式环境中的性能优化策略,以及如何通过灵活的任务调度机制提升系统效率。最后,结合具体实例,展示了 TBSchedule 在实际项目中的应用效果,为开发者提供了宝贵的实践经验。 ... [详细]
  • 在处理高并发场景时,确保业务逻辑的正确性是关键。本文深入探讨了Java原生锁机制的多种细粒度实现方法,旨在通过使用数据的时间戳、ID等关键字段进行锁定,以最小化对系统性能的影响。文章详细分析了不同锁策略的优缺点,并提供了实际应用中的最佳实践,帮助开发者在高并发环境下高效地实现锁机制。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 深入解析 Android 中 EditText 的 getLayoutParams 方法及其代码应用实例 ... [详细]
  • 在托管C++中开发应用程序时,遇到了如何声明和操作字符串数组的问题。本文详细探讨了字符串数组在托管C++中的应用与实现方法,包括声明、初始化、遍历和常见操作技巧,为开发者提供了实用的参考和指导。 ... [详细]
  • 在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ... [详细]
author-avatar
liuliyu2000_867
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有