精华内容
下载资源
问答
  • 可以看到无论是大厂还是小厂职位JD,都会出现多诸如线程并发、线程池、锁这样字眼和要求。其次,几乎所有程序都或多或少需要并发和多线程,尤其是如果用户量大,而不采用并发,那么程序性能很快就会出现...

    f53dc278c0c379922c2ca963e6210e70.png

    者 | 慕课网

    来源 | 慕课网

    4ea406d5eaa2d8270744c3bbc5bf569e.gif

    并发是程序员永恒的命题。为什么学习并发这么重要呢?

    首先,并发是面试过程中必考的考点。可以看到无论是大厂还是小厂的职位JD中,都会出现多诸如线程、并发、线程池、锁这样的字眼和要求。

    其次,几乎所有程序都或多或少需要并发和多线程,尤其是如果用户量大,而不采用并发,那么程序的性能很快就会出现瓶颈。

    最后,并发是很多框架的原理和基础,比如在Spring中会有很多对线程池、单例的应用,把并发学透,在学习框架的时候就可以一通百通。

    所以程序员在职场打怪升级的过程中,并发基本上是绕不过去的Boss

    这次我们请来了资深Java开发工程师、慕课网Java精英讲师--悟空来给大家分享Java并发的知识。

    原价199元,限时0元

    扫码添加小姐姐免费报名本次直播

    2947f83ae4e5eba13baf8f11314ea6c4.pngb2327d1eeddfd66837df33a1d33af88b.png

    ★ 讲师介绍

    2b7352d191dcd5fe5b84c84d32964c06.png

    悟空(资深Java开发工程师)

    硕士毕业于世界排名Top 9的慕尼黑工业大学(计算机学科排名),国内毕业于同济大学,成绩专业排名Top 1,获公派留学全额奖学金,现就职于一线明星互联网公司。课程深入浅出、系统全面、通俗易懂、干货满满。讲课充满热情,声情并茂,生动有趣。

    直播主题

    • 从零叩开Java并发大门

    • 究竟有几种创建线程的方法?

    直播时间

    6月21日(周日)晚8点

    适合人群

    零基础/转行Java工程师/补基础

    直播大纲

    1、开篇立题

    √ 互联网大厂薪资和职级介绍

    2、主题分享

    √ 进程、线程、并发的异同?

    √ 究竟有几种创建线程的方法?

    √ 为什么市面上多数说法都是错的?

    3、面试指南,避坑经验

    √ 为啥明明感觉回答对了却没通过面试?

    √ 面试官喜欢具有什么素质的候选人?

    √ 面试前需要做哪些准备?

    你将获得

    1、了解一线大厂薪资职级情况

    2、get面试成功的正确姿势

    3、弄清进程、线程、并发的异同

    4、得解究竟有几种创建线程的方法?

    参与方式

    前500名同学可以免费参加

    立即扫码报名

    2947f83ae4e5eba13baf8f11314ea6c4.png9f6c7b69d959095a5fb0d894319bfe4d.pngfa814ca65f03f17df713e18f8da0867a.png47675011c0028d52eb96475a8e0bf8ca.png

    END

    ● 求求大家别再“妖魔化”Python了

    ● 前端工程师如何抗住HTML、CSS、JS三连击?

    ● 我说了Java创建线程的这几种方式,面试官夸我棒

    ● 程序员如何在大环境恶劣下脱颖而出?(3000字干货)

    96510cf5b18aaab950fa596a9fdd2de7.png

    e67a40236986bed093c53a8ac2c5a5db.gif

    展开全文
  • 不同的线程可以执行同样的函数。 进程:是资源分配的基本单位。当一个程序开始运行时,它就是一个进程进程包括运行中的程序和程序所使用到的内存和系统资源。CPU是以时间片的方式为进程分配CU...

    一:线程与进程

    1.概念

    线程:是程序执行流的最小单元,是系统独立调度和分配CPU(独立运行)的基本单位。线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,
    即不同的线程可以执行同样的函数。

    进程:是资源分配的基本单位。当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。CPU是以时间片的方式为进程分配CUP处理时间。
    而一个进程又是由多个线程所组成的。

    2.区别:

    1.线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。

    2.每个进程都有自己一套独立的资源(数据),供其内的所有线程共享。

    3.不论是大小,开销线程要更“轻量级”

    4.一个进程内的线程通信比进程之间的通信更快速,有效。(因为共享变量)

    (面试重点 必须从cpu调度,上下文切换,数据共享,多核cup利用率,资源占用,等等各方面回答,然后有一个问题必须会被问到:哪些东西是一个线程私有的?答案中必须包含寄存器,否则悲催)

    多线程:

    • 高效的内存共享,数据共享
    • 较轻的上下文切换开销,不用切换地址空间,不用更改CR3寄存器(寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令数据地址),不用清空TLB。
    • 创建销毁切换比较简单

    多进程:

    • 更强的容错性,不会一阻全阻,一个进程崩溃不会整个系统崩溃。

    • 更好的多核伸缩性,进程的使用将许多内核资源(如地址空间,页表,打开的文件)隔离,在多核系统上的可伸缩性强于多线程程序

    • 在多核利用率上,多进程和多线程同样可以提高多核利用率。 
      其实对于创建和销毁,上下文切换,其实在Linux系统下差别不大,Window下有较大差别。 
      综上,多进程和多线程的最主要的区别就在资源共享,隔离问题。如果工作使用的内存较大,使用多线程可以避免CPU 的cache的换入换出,从而提高性能。

    线程私有

    • ID,每个线程都有自己的ID作为进程中唯一的表述。 

    • 一组寄存器值,用来保存状态
    • 各自的堆栈
    • 错误返回码,防止错误还未被处理就被其他线程修改。
    • 信号屏蔽码,每个线程感兴趣的信号不同。
    • 优先级
    • 共享的:进程的代码段,公有数据,进程打开的文件描述符,全局内存,进程的栈,堆内存等。

    可重入:可重入函数也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有多个该函数的副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。

    线程安全的条件:概念比较直观。一般说来,一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。

    要确保函数线程安全,主要需要考虑的是线程之间的共享变量。属于同一进程的不同线程会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器。因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态变量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式。

    可重入的判断条件

    要确保函数可重入,需满足一下几个条件:

    1、不在函数内部使用静态或全局数据 
    2、不返回静态或全局数据,所有数据都由函数的调用者提供。 
    3、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
    4、不调用不可重入函数。

    可重入与线程安全并不等同,一般说来,可重入的函数一定是线程安全的,但反过来不一定成立。它们的关系可用下图来表示:

    比如:strtok函数是既不可重入的,也不是线程安全的;加锁的strtok不是可重入的,但线程安全;而strtok_r既是可重入的,也是线程安全的。
    如果我们的线程函数不是线程安全的,那在多线程调用的情况下,可能导致的后果是显而易见的——共享变量的值由于不同线程的访问,可能发生不可预料的变化,进而导致程序的错误,甚至崩溃。
     

    关于线程的堆栈

    说一下线程自己的堆栈问题。

    是的,生成子线程后,它会获取一部分该进程的堆栈空间,作为其名义上的独立的私有空间。(为何是名义上的呢?)由于,这些线程属于同一个进程,其他线程只要获取了你私有堆栈上某些数据的指针,其他线程便可以自由访问你的名义上的私有空间上的数据变量。(注:而多进程是不可以的,因为不同的进程,相同的虚拟地址,基本不可能映射到相同的物理地址)

    二.多线程与多进程

    多线程:同一时刻执行多个线程。用浏览器一边下载,一边听歌,一边看视频,一边看网页。。。

    多进程:同时执行多个程序。如,同事运行YY,QQ,以及各种浏览器。

     

    三.并发与并行

    并发当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。


    并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

    四. 多线程优缺点

    多线程的好处:
    可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,
    这样就大大提高了程序的效率。 


    多线程的不利方面:
    线程也是程序,所以线程需要占用内存,线程越多占用内存也越多; 
    多线程需要协调和管理,所以需要CPU时间跟踪线程; 
    线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
    线程太多会导致控制太复杂,最终可能造成很多Bug;
     

    展开全文
  • 概念 多线程(threading)和多进程(multiprocessing) 进程是指一个内存中运行的应用程序,比如在Windows系统中,一个...而线程则是指进程中的一个执行流程,一个进程可以有多个线程,每个线程分别执行不同的任务,当进...
    • 概念
      多线程(threading)和多进程(multiprocessing)
      进程是指一个内存中运行的应用程序,比如在Windows系统中,一个运行的exe就是一个进程。
      线程是指进程中的一个执行流程。
      联系与区别:
    1. 一个程序至少有一个进程,而一个进程至少有一个线程。一个应用程序可以同时启动多个进程。而线程则是指进程中的一个执行流程,一个进程可以有多个线程,每个线程分别执行不同的任务,当进程内的多个线程同时运行时,这种运行方式就被称为并发运行
    2. 每个进程在执行过程中都拥有独立的内存单元,而同一个进程中的多个线程则共享内存,从而极大地提高了程序的运行效率。

    多线程并发
    伪并发是指单核处理器的并发;
    真并发是指多核处理器的并发。
    多线程并发只是表面和感觉上的并发,并不是实质上的并发, 单CPU的,一次最多只能有一个线程获取CPU并运行,但是它在多个线程之间频繁切换,当切换的频率高到一定程度时,我们就感觉所有的线程在同时运行,于是感觉这多个线程是并发的。
    多线程的实质是“最大限度地利用CPU资源,当某一个线程的处理不需要占用CPU而只需要和I/O等资源打交道时,让其他线程有机会获得CPU资源。

    1. 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。

    2. 互斥:进程间相互排斥的使用临界资源的现象,就叫互斥。

    3. 同步:进程之间的关系是相互依赖的关系。进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。

    4. 并行: 在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行在多处理器上的程序才可实现并行处理。并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也是说并发事件之间不一定要同一时刻发生

    5. 多线程 : 进程中并发运行的一段代码, 多线程可以实现线程间的切换执行。

    **

    异步与同步:

    • 同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。
    • 异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。
    • 多线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。

    并发和并行都可以是很多个线程,就看这些线程能不能同时被(多个)CPU执行,可以说明是并行,并发是多个线程被一个CPU轮流切换着执行

    高并发的相关指标
    高并发相关指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等

    响应时间:系统对某个请求做出相应的时间.例如处理一个HTTP请求,从HTTP发出到收到响应需要200ms,则200ms就是系统的响应时间.

    吞吐量:单位时间内处理的请求数量

    QPS:每秒响应请求数,这个感觉和吞吐量区分不大

    并发用户数:同时承载正常使用系统功能的用户数量. 例如微信,同时可以x亿人在线,一定程度代表了系统的并发用户数

    提高并发能力
    有以下途径:

    增加机器:读写分离,分布式部署(不同模块),集群(相同服务)
    增强单机性能:数据库优化,优化代码(如算法),硬件升级(CPU,内存,SSD等)

    展开全文
  • 进程、线程、多线程并发与并行、线程运行内存模型、锁 1.1我们为什么需要多线程? 比如我们在听歌时可以一边听一边下载,这种一个程序看起来同时做好几件事的...线程进程中的一个实体,是被系统独立调度和分派的

    进程、线程、多线程、并发与并行、线程运行内存模型、锁

    1.1我们为什么需要多线程?
    比如我们在听歌时可以一边听一边下载,这种一个程序看起来同时做好几件事的情况,就需要多线程。

    线程(Thread)和进程(Process)的关系很紧密,进程和线程是两个不同的概念,进程的范围大于线程。通俗地说,进程就是一个程序,线程是这个程序能够同时做的各件事情。比如,媒体播放机运行时就是一个进程,而媒体播放机同时做的下载文件和播放歌曲,就是两个线程。因此,可以说进程包含线程。

    线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所有的全部资源。一个进程中是可以拥有很多线程的。

    1.2实现多线程的方法

    实现多线程有很多方法:Thread 继承 、Runnable 接口实现 、Callable 接口实现(带返回值) 、线程池(提供线程)。
    我们要讲的就是其中的两种: Thread 继承 、Runnable 接口实现

    1.2.1继承Thread 类开发多线程

    我们可以用多线程来解决“程序看起来同时做好几件事情”的效果,只需要将各个文件传送的工作分别写在线程里即可。

    继承 Thread 类实现多线程,该方法步骤如下:
    、编写一个类,继承 java.lang.Thread
    、在这个类中,重写 java.lang.Thread 类中的以下函数:
    public void run()

    class FileTransThread extends Thread{
          private String fileName;
          public FileTransThread(String fileName){ 
              this.fileName = fileName;
          }
          public void run(){
              System.out.println("传送" + fileName); 
                 try{
                    Thread.sleep(1000 * 10);
                    }
                    catch(Exception ex){}
                    System.out.println(fileName + "传送完毕");
          }
     }
    

    线程编写完毕

    、实例化线程对象,调用其 **start()**函数来启动该线程。

    FileTransThread ft1 = new FileTransThread(“文件 1);
    ft1.start();
    

    1.2.2实现Runnable 接口开发多线程

    该方法实现步骤如下:
    、编写一个类,实现 java.lang.Runnable 接口
    、在这个类中,重写 java.lang. Runnable 接口中的以下函数:
    public void run()
    将线程需要执行的代码放入run方法。

    class FileTransRunnable implements Runnable{ 
          private String fileName ;
          public FileTransRunnable(String fileName){ 
              this.fileName = fileName ;
          }
          public void run(){
              System.out.println("传送" + fileName); 
              try{
                   Thread.sleep(1000 * 10);
                 }catch(Exception ex){}
              System.out.println(fileName + "传送完毕");
          }
    }
    

    、实例化 java.lang.Thread 对象,实例化上面编写的 Runnable 实现类,将后者传入 Thread 对象的构造函数。调用 Thread 对象的 start方法来启动线程。

    FileTransRunnable fr1 = new FileTransRunnable("文件 1");
    Thread th1 = new Thread(fr1);
    th1.start();
    

    1.2.3两种方法的不同之处

    、继承 Thread 的方法,具有如下特点:

    每一个对象都是一个线程,其对象具有自己的成员变量。比如:

    class FileTransThread extends Thread{ private String fileName;
    public void run(){
    }
    FileTransThread ft1 = new FileTransThread(); 
    FileTransThread ft2 = new FileTransThread();
    

    此时,线程 ft1ft2 具有各自的 fileName 成员变量,除非将 fileName 定义为静态变量。

    Java 不支持多重继承,继承了 Thread,就不能继承其他类。因此,该类主要完成线程工作,功能比较单一。

    二、实现 Runnable 的方法,具有如下特点:

    每一个对象不是一个线程,必须将其传入 Thread 对象才能运行,各个线程是否共享 Runnable 对象成员,视情况而定。比如有如下 Runnable 类:

    class FileTransRunnable extends Runnable{ 
          private String fileName ;
          public void run(){
          }
    }
    

    下面代码:

    FileTransRunnable fr1 = new FileTransRunnable(); 
    FileTransRunnable fr2 = new FileTransRunnable(); 
    Thread th1 = new Thread(fr1);
    Thread th2 = new Thread(fr2);
    

    此时,线程 th1th2 访问各自的 fileName 成员变量,因为它们传进来的FileTransRunnable 是不同的。但是如下代码:

    FileTransRunnable fr = new FileTransRunnable();
    Thread th1 = new Thread(fr);
    Thread th2 = new Thread(fr);
    

    线程 th1th2 访问的确实同一个 fileName 成员变量,因为它们传进来的FileTransRunnable 是同一个对象。

    Java 不支持多重继承,却可以支持实现多个接口,因此,我们有时可以给一些继承了某些父类的类,通过实现 Runnable 的方法增加线程功能。

    1.3并发和并行

    并发是指两个或多个事件在同一时间间隔内发生。操作系统的并发性是指计算机系统中同时存在多个运行的程序,因此它具有处理和调度多个程序同时执行的能力。在操作系统的目的是使程序能并发执行。

    并发是指同一时间间隔,并行是指同一时刻。在多线程环境下,一段时间内宏观上有多个线程在同时执行,而在每个时刻,单处理机环境下实际仅能有一道程序执行,因此微观上这些线程仍是分时交替执行的。操作系统的并发性是通过分时得以实现的。并行性是指系统具有同时进行运算或操作的特性,在同一时刻能完成两种或两种以上的工作。并行需要相关硬件支持,多个CPU

    2.线程运行的内存模型和硬件架构

    2.1线程运行的内存架构

    每一个运行在Java虚拟机里的线程都拥有自己的线程栈。这个线程栈包含了这个线程调用的方法当前执行点相关的信息。一个线程仅能访问自己的线程栈。一个线程创建的本地变量对其它线程不可见,仅自己可见。即使两个线程执行同样的代码,这两个线程任然在在自己的线程栈中的代码来创建本地变量。因此,每个线程拥有每个本地变量的独有版本。

    所有原始类型的本地变量都存放在线程栈上,因此对其它线程不可见。一个线程可能向另一个线程传递一个原始类型变量的拷贝,但是它不能共享这个原始类型变量自身。

    堆上包含在Java程序中创建的所有对象,无论是哪一个对象创建的。这包括原始类型的对象版本。如果一个对象被创建然后赋值给一个局部变量,或者用来作为另一个对象的成员变量,这个对象任然是存放在堆上。

    一个本地变量可能是原始类型,在这种情况下,它总是“呆在”线程栈上。一个本地变量也可能是指向一个对象的一个引用。在这种情况下,引用(这个本地变量)存放在线程栈上,但是对象本身存放在堆上。一个对象可能包含方法,这些方法可能包含本地变量。这些本地变量任然存放在线程栈上,即使这些方法所属的对象存放在堆上。一个对象的成员变量可能随着这个对象自身存放在堆上。不管这个成员变量是原始类型还是引用类型。静态成员变量跟随着类定义一起也存放在堆上。

    存放在堆上的对象可以被所有持有对这个对象引用的线程访问。当一个线程可以访问一个对象时,它也可以访问这个对象的成员变量。如果两个线程同时调用同一个对象上的同一个方法,它们将会都访问这个对象的成员变量,但是每一个线程都拥有这个本地变量的私有拷贝。

    下图演示了调用栈和本地变量存放在线程栈上,对象存放在堆上。
    在这里插入图片描述
    两个线程拥有一些列的本地变量。其中一个本地变量(本地变量2)执行堆上的一个共享对象(对象3)。这两个线程分别拥有同一个对象的不同引用。这些引用都是本地变量,因此存放在各自线程的线程栈上。这两个不同的引用指向堆上同一个对象。

    注意,这个共享对象(对象3)持有对象2和对象4一个引用作为其成员变量(如图中对象3指向对象2和对象4的箭头)。通过在对象3中这些成员变量引用,这两个线程就可以访问对象2和对象4。

    这张图也展示了指向堆上两个不同对象的一个本地变量。在这种情况下,指向两个不同对象的引用不是同一个对象。理论上,两个线程都可以访问对象1和对象5,如果两个线程都拥有两个对象的引用。但是在上图中,每一个线程仅有一个引用指向两个对象其中之一。

    2.2硬件内存架构
    下图是硬件架构的简单图示:
    在这里插入图片描述
    每个CPU都包含一系列的寄存器,它们是CPU内内存的基础。CPU在寄存器上执行操作的速度远大于在主存上执行的速度。这是因为CPU访问寄存器的速度远大于主存。

    每个CPU可能还有一个CPU缓存层。实际上,绝大多数的现代CPU都有一定大小的缓存层。CPU访问缓存层的速度快于访问主存的速度,但通常比访问内部寄存器的速度还要慢一点。一些CPU还有多层缓存,但这些对理解Java内存模型如何和内存交互不是那么重要。只要知道CPU中可以有一个缓存层就可以了。

    一个计算机还包含一个主存。所有的CPU都可以访问主存。主存通常比CPU中的缓存大得多。

    3.锁

    3.1为什么需要锁

    这涉及到了线程同步(synchronize)问题。我们通过一个例子来了解一下这个现象。

    有若干张飞机票,2 个线程去卖它们,要求没有票时能够提示: 没有票了。以最后剩下 3 张票为例。

    class TicketRunnable implements Runnable{ 
          private int ticketNum = 3;	//以 3 张票为例
          public void run(){
                 while(true){
                       String tName = Thread.currentThread().getName();
                       if(ticketNum<=0){
                               System.out.println(tName + “无票”);
                               break;
                            }else{
                                try{
                                    Thread.sleep(1000);//程序休眠 1000 毫秒
                                   }catch(Exception ex){} 
                  ticketNum--;
                  System.out.println(tName + “卖出一张票,还剩” + ticketNum + “张票”);
                         }
               }
          }
     }
     
    public class ThreadSynTest2 {
              public static void main(String[] args){ 
              TicketRunnable tr = new TicketRunnable(); 
              Thread th1 = new Thread(tr,” 线 程 1); 
              Thread th2 = new Thread(tr,” 线 程 2); th1.start();
              th2.start();
          }
     }
    

    运行结果如下:
    在这里插入图片描述
    一张票被卖出两次,甚至出现负数。显然结果不可靠。这时候我们就需要锁。

    3.2如何解决上述问题

    怎样解决这个问题?很简单,就是让一个线程卖票时其他线程不能抢占 CPU。根据定义,实际上相当于要实现线程的同步,通俗地讲,可以给共享资源(在本例中为票)加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
    有一种比较直观的方法,可以在共享资源(如“票”)每一个对象内部都增加一个新成员,标识“票”是否正在被卖中,其他线程访问时,必须检查这个标识,如果这个标识确定票正在被卖中,线程不能抢占 CPU。这种设计理论上当然也是可行,但由于线程同步的情况并不是很普遍,仅仅为了这种小概率事件,在所有对象内部都开辟另一个成员空间,带来极大的空间浪费,增加了编程难度,所以,一般不采用这种方法。现代的编程语言的设计思路都是把同步标识加在代码段上,确切的说,是把同步标识放在“访问共享资源(如卖票)的代码段”上。
    在 Java 语言中,synchronized 关键字可以解决这个问题,整个语法形式表现为:

    synchronized(同步锁对象) {
    // 访问共享资源,需要同步的代码段
     	}	
    

    注意,synchronized 后的“同步锁对象”,必须是可以被各个线程共享的,如 this、某个全局标量等。不能是一个局部变量。
    其原理为:当某一线程运行同步代码段时,在“同步锁对象”上置一标记,运行完这段代码,标记消除。其他线程要想抢占 CPU 运行这段代码,必须在“同步锁对象” 上先检查该标记,只有标记处于消除状态,才能抢占 CPU。在上面的例子中,this 是一个“同步锁对象”。
    因此,在上面的案例中,可以将将卖票的代码用 synchronized 代码块包围起来,
    “同步锁对象”取 this。代码如下:

    class TicketRunnable implements Runnable { 
          private int ticketNum = 3; // 以 3 张票为例
          public void run() {
              while (true) {
                  String tName = Thread.currentThread().getName();
                 // 将需要独占 CPU 的代码用 synchronized(this)包围起来
                  synchronized (this) {
                     if (ticketNum <= 0) { 
                       System.out.println(tName + “票”); break;
                     } else {
                           try {
                               Thread.sleep(1000);// 程序休眠 1000 毫秒
                          } catch (Exception ex) {}
                   ticketNum--; 
                   System.out.println(tName + “卖出一张票,还剩” +ticketNum “张票”);
                   }
                }
            }
          }
    }
    
    public class ThreadSynTest3 {
        public static void main(String[] args) { 
            TicketRunnable tr = new TicketRunnable(); 
            Thread th1 = new Thread(tr, “ 线 程 1);
            Thread th2 = new Thread(tr, “线程 2);
            th1.start();
            th2.start();
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    从以上代码可以看出,该方法的本质是将需要独占CPU 的代码用**synchronized(this)**包围起来。如前所述,一个线程进入这段代码之后,就在 this 上加了一个标记,直到该线程将这段代码运行完毕,才释放这个标记。如果其他线程想要抢占 CPU,先要检查 this 上是否有这个标记。若有,就必须等待。
    实际上,在 Java 内,还可以直接把 synchronized 关键字直接加在函数的定义上, 这也是一种可以推荐的方法。如:

    public synchronized void f1() {
    // f1 代码段
    }
    

    效果等价于:

    public void f1()	{ 
        synchronized(this){
    // f1 代码段
        }
    }
    

    3.3Lock类

    我们也可以使用Java中的Lock类,使用方法如下:

    public void number(){
    		//添加锁
    		lock.lock();
    		//原子操作   volatile
    		data.count++;
    		System.out.println(Thread.currentThread().getName()+": "+data.count);
    	
    		//释放锁
    		lock.unlock();
    	}
    
    

    将需要独占资源的代码写在lock方法与unlock方法之间。
    原子操作:原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程。

    展开全文
  • 这个进程中不同的子功能可以使用不同的线程去完成(如聊天程序中,一个线程发送消息,一个线程接收消息)。PS:线程是一个轻量级的进程。进程是对于操作系统的程序运行单元,线程是对于编程语言的运行单元。(如可...
  • 一、线程与进程的区别 先简单说说线程与进程的概念: ...而线程则是指进程中的一个执行流程,一个进程可以有多个线程,每个线程分别执行不同的任务,当进程内的多个线程同时运行时,这种运行方式就被称为并发运行。
  • 10.3.2 确定当前线程 ...如果服务器进程中有多个服务线程处理不同的操作,那么在这样服务器进程中,对线程命名就很有用。 import threading import time def worker(): print(threading.current_thread(...
  • 好程序员Python培训分享Python中进程和线程... 一个进程中可以并发多条线程,每条线程并行执行不同的任务。 Num02–>进程 进程就是一个程序在一个数据集上一次动态执行过程。 进程有以下三部分组成: 1,...
  • 在 Java 中,当我们启动 main 函数时其实就是启动了⼀个 JVM 的进程,⽽ main 函数所在的线程就是这个进程中的⼀个线程,也称主线程 什么是线程?线程与进程相似,但线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执...
  • 在本文你将看到:什么是线程进程,区别...一个进程在其执行过程中可以产生多个线程。与进程不同的是同类多个线程共享进程的堆和方法区资源,但每个线程有自己程序计数器、虚拟机栈和本地方法栈,所以系统...
  • 一个进程中可以并发多条线程,每条线程并行执行不同的任务。Num02–>进程进程就是一个程序在一个数据集上一次动态执行过程。进程有以下三部分组成:1,程序:我们编写程序用来描述进程要完成哪些功能以及如何...
  • 每天进步一点点! .进程和线程的区别是什么? 进程: 一个程序下至少有一个进程,进程是程序一次执行过程,是系统...从上图中可以看到:一个进程中可以由多个线程,多个线程共享进程堆和方法区资源,但是每个线
  • 线程共享的环境包括:进程代码段、进程的共有数据(利用这些...每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。2、寄存器组的值 由于线程间是并发运行的,每个线程有自己不同的运行线索,当
  • 第一:Java多线程面试问题1:进程和线程之间有什么不同?...线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。2:多线程编程的好处是什么?在多线程程序中,多个线程并发的执行以提...
  • 进程是针对用户概念,是可以看得见,比如用户打开了一个应用程序,就是打开了一个进程,计算机打开任务管理器,可以看到正在运行各种进程,但是对于CPU(单核)来说,一个时刻只能运行一个进程,看起来很多...
  • 并发进程线程

    2019-11-25 19:03:57
    并发 在同一时间段内多个任务同时发生在单核处理器的计算机中,同一时刻只能执行一个任务。并发是由于计算机快速切换任务造成多个任务同时执行的...进程就是指程序的运行过程,执行中的程序称为进程,每个进程都...
  • 一个进程中可以并发多条线程,每条线程并行执行不同的任务。 Num02–>进程 进程就是一个程序在一个数据集上一次动态执行过程。 进程有以下三部分组成: 1,程序:我们编写程序用来描述进程要完成哪些功能以及...
  • 原标题:Python中进程和线程详解好程序员Python培训分享...一个进程中可以并发多条线程,每条线程并行执行不同的任务。Num02–>进程进程就是一个程序在一个数据集上一次动态执行过程。进程有以下三部分组成...
  • 线程是面试必问知识点,多多少少都会有人掉进面试...不同进程中哪些资源可以共享堆】由于堆是在进程空间中开辟出来,所以它是理所当然地被共享;因此new出来都是共享(16位平台上分全局堆和局部堆,局部...
  • Java多线程面试问题1. 进程和线程之间有什么不同?...线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。2. 多线程编程的好处是什么?在多线程程序中,多个线程并发的执行以提高...
  • 关注我,可以获取最新知识、经典面试题以及技术分享 多线程并发是求职大小厂面试必问知识点,其涉及到点很多,难度很大。有些人面对这些问题有点迷茫,为了解决这情况,总结了一下java多线程并发的基础知识点...
  • 线程并发问题是Java技术面试中面试官比较喜欢问问题之一。在这里,从面试角度列出了大部分重要问题,但是你仍然应该牢固掌握Java多线程基础知识来对应...而线程是在进程中执行一个任务。Java运行环...
  • 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,...
  • 在Java,当我们启动一个main函数其实就是启动了一个JVM进程,main函数所在的线程就是这个进程的一个线程,也叫主线程。更直观的就是,打开电脑的任务管理器,就可以看到当前Windows的运行进程。 (2)线程:线程...
  • 1. 进程和线程之间有什么不同?...线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。2. 多线程编程的好处是什么?在多线程程序中,多个线程并发的执行以提高程序的效率,CPU不会...
  • 进程线程的区别

    2021-02-02 19:05:17
    不同进程中线程切换会引起进程切换。 3、拥有资源:进程是拥有资源基本单位,线程不拥有系统资源,但线程可以访问其隶属进程系统资源。 4、并发性:不仅线程之间可以并发执行,多个线程之间也可以并发
  • 一条线程进程中一个单一顺序控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个线程是一个execution context(执行上下文),即一个cpu执行时所需要一串指令。 1.1.2 线程的工作方式 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,229
精华内容 891
关键字:

不同进程中的线程可以并发