精华内容
下载资源
问答
  • 实验的主要任务,是实现一个多线程的搜索应用,利用处理器的多线程提高运行效能。 请首先建lcc项目,SearchRandom,选择优化,运行.
  • Java多线程原理

    2020-09-19 21:00:02
    关于Java多线程知识的总结。


    文章大部分内容摘自gitbook:深入浅出Java多线程


    Java内存模型基础知识

    并发编程模型的两个关键问题

    并发编程模型的两个关键问题

    1:线程之间如何交换信息(线程间如何通信?)
    2:线程之间如何同步(线程间如何通信?)

    有两种并发模型可以解决这两个问题

    • 消息传递并发模型
    • 共享内存并发模型
    通信 同步
    消息传递并发模型 线程间通过发送消息来显式进行通信 发送消息天然同步,因为发送消息总是在接受消息之前,因此同步是隐式的
    共享内存并发模型 线程之间共享程序的公共状态,通过读-写内存中的公共状态进行隐式通信 必须显示指定某段代码需要在线程之间互斥执行,同步是显式的

    在Java中,使用的是共享内存并发模型。

    Java内存模型的抽象结构

    Java运行时内存的划分图如下:

    在这里插入图片描述
    对于每一个线程来说,栈都是私有的,堆是共有的。也就是说在栈中的变量(局部变量、方法定义参数、异常处理器参数)不会在线程之间共享,而在堆中的变量是共享的,本文称为共享变量。

    Java内存模型图如下:

    Java线程之间的通信由Java内存模型(简称JMM)控制,从抽象的角度来说,JMM定义了线程和主内存之间的抽象关系。

    在这里插入图片描述
    从图中可以看出:

    1. 所有的共享变量都存在主内存中。
    2. 每个线程都保存了⼀份该线程使⽤到的共享变量的副本。
    3. 如果线程A与线程B之间要通信的话,必须经历下⾯2个步骤:
      i. 线程A将本地内存A中更新过的共享变量刷新到主内存中去。
      ii. 线程B到主内存中去读取线程A之前已经更新过的共享变量。

    所以,线程A⽆法直接访问线程B的⼯作内存,线程间通信必须经过主内存。注意,根据JMM的规定,线程对共享变量的所有操作都必须在⾃⼰的本地内存中进⾏,不能直接从主内存中读取。

    所以线程B并不是直接去主内存中读取共享变量的值,⽽是先在本地内存B中找到这个共享变量,发现这个共享变量已经被更新了,然后本地内存B去主内存中读取这个共享变量的新值,并拷⻉到本地内存B中,最后线程B再读取本地内存B中的新值。

    那么怎么知道这个共享变量的被其他线程更新了呢? 这就是JMM的功劳了,也是JMM存在的必要性之⼀。JMM通过控制主内存与每个线程的本地内存之间的交互,来提供内存可⻅性保证。

    Java中的volatile关键字可以保证多线程操作共享变量的可⻅性以及禁⽌指令重排序,synchronized关键字不仅保证可⻅性,同时也保证了原⼦性(互斥性)。 在更底层,JMM通过内存屏障来实现内存的可⻅性以及禁⽌重排序。为了程序员的⽅便理解,提出了happens-before,它更加的简单易懂,从⽽避免了程序员为了理解内存可⻅性⽽去学习复杂的重排序规则以及这些规则的具体实现⽅法。

    Java运行时内存划分和JMM(Java内存模型)之间的区别与联系

    • 联系:
      都存在私有数据区域和共享数据区域。⼀般来说,JMM中的主内存属于共享数据区域,他是包含了堆和⽅法区;同样,JMM中的本地内存属于私有数据区域,包含了程序计数器、本地⽅法栈、虚拟机栈。

    • 区别:
      两者是不同的概念层次。JMM是抽象的,他是⽤来描述⼀组规则,通过这个规则来控制各个变量的访问⽅式,围绕原⼦性、有序性、可⻅性等展开的。⽽Java运⾏时内存的划分是具体的,是JVM运⾏Java程序时,必要的内存划分。


    happens-before

    什么是happens-before

    happens-before式JMM(Java内存模型)提供的happens-before规则(JSR-133规范)。满⾜了程序员的需求——简单易懂,并且提供了⾜够强的内存可⻅性保证。换⾔之,程序员只要遵循happens-before规则,那他写的程序就能保证在JMM中具有强的内存可⻅性。JMM可以通过happens-before关系向程序员提供跨线程的内存可⻅性保证。

    happens-before关系的定义如下:

    1. 如果⼀个操作happens-before另⼀个操作,那么第⼀个操作的执⾏结果将对第⼆个操作可⻅,⽽且第⼀个操作的执⾏顺序排在第⼆个操作之前。
    2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执⾏。如果重排序之后的执⾏结果,与按happens-before关系来执⾏的结果⼀致,那么JMM也允许这样的重排序。

    总之,如果A操作happens-before操作B,那么操作A在内存中所作的操作对B都是可见的,不管他们是不是在同一个线程。

    天然的happens-before关系

    在Java中,存在一些天然的happens-before关系:

    • 程序顺序规则:⼀个线程中的每⼀个操作,happens-before于该线程中的任意后续操作。
    • 监视器锁规则:对⼀个锁的解锁,happens-before于随后对这个锁的加锁。
    • volatile变量规则:对⼀个volatile域的写,happens-before于任意后续对这个volatile域的读。
    • 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
    • start规则:如果线程A执⾏操作ThreadB.start()启动线程B,那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
    • join规则:如果线程A执⾏操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

    volatile关键字

    volatile关键字的内存含义

    在Java中,volatile关键字有特殊的内存语义。volatile主要有以下两个功能:

    • 保证变量的内存可⻅性
    • 禁⽌volatile变量与普通变量重排序(JSR133提出,Java 5 开始才有这个“增强的volatile内存语义”)

    1:内存可见性

    public class VolatileExample {
    	
    	int a = 0;
    //	使用volatile关键字设置一个值
    	volatile boolean flag = false;
    	
    	public void write() {
    		a = 1;
    //		当⼀个线程对  volatile  修饰的变量(flag)进⾏写操作
    //		下面这个操作JMM会⽴即把该线程对应的本地内存中的共享变量的值刷新到主内存
    		flag = true;
    	}
    	
    	public void read() {
    //		当⼀个线程对  volatile  修饰的变量(flag)进⾏读操作
    //		JMM会把⽴即该线程对应的本地内存置为⽆效,从主内存中读取共享变量的值
    		if (flag) {
    			System.out.println(a);
    		}
    	}
    
    }
    

    通过上面的代码看出,所谓内存可⻅性,指的是当⼀个线程对 volatile 修饰的变量进⾏写操作时,JMM会⽴即把该线程对应的本地内存中的共享变量的值刷新到主内存;当⼀个线程对 volatile 修饰的变量进⾏读操作时,JMM会把⽴即该线程对应的本地内存置为⽆效,从主内存中读取共享变量的值。

    2:禁止重排

    如果volatile变量与普通变量发⽣了重排序,虽然volatile变量能保证内存可⻅性,也可能导致普通变量读取错误。

    所以在旧的内存模型中,volatile的写-读就不能与锁的释放-获取具有相同的内存语义了。为了提供⼀种⽐锁更轻量级的线程间的通信机制,JSR-133专家组决定增强volatile的内存语义:严格限制编译器和处理器对volatile变量与普通变量的重排序。

    volatile的用途

    从volatile的内存语义上来看,volatile可以保证内存可⻅性且禁⽌重排序。

    在保证内存可⻅性这⼀点上,volatile有着与锁相同的内存语义,所以可以作为⼀个“轻量级”的锁来使⽤。但由于volatile仅仅保证对单个volatile变量的读/写具有原⼦性,⽽锁可以保证整个临界区代码的执⾏具有原⼦性。所以在功能上,锁⽐volatile更强⼤;在性能上,volatile更有优势。


    Synchronized关键字

    Java多线程的锁都是基于对象的,Java中的每一个对象都可以作为一个锁。

    Synchronized关键字

    Synchronized关键字的三种形式:

    关键字在实例方法上,锁为当前实例

    public synchronized void instanceLock() {
    	// code
    }
    

    关键字在静态方法上,锁为class对象

    public static synchronized void classLock() {
    	// code
    }
    

    关键字在代码块上,锁是括号中的对象

    // 关键字在代码块上,锁是括号中的对象
    public void blockLock() {
    	Object object = new Object();
    	synchronized (object) {
    		// code
    	}
    }
    

    在Java6之后,对象其实有四种锁,它们的级别由低到高依次是:

    1:无锁状态
    2:偏向锁状态
    3:轻量锁状态
    4:重量锁状态

    几种锁会随着竞争情况逐渐升级,锁的升级很容易发生,但是锁降级发生的条件会比较苛刻,锁降级发生在Stop The World期间,当JVM进⼊安全点的时候,会检查是否有闲置的锁,然后进行降级。

    偏向锁

    是为了在资源没有被其他线程竞争的情况下尽量减少锁带来的性能开销。在锁对象的对象头中有一个ThreadId字段,当第一个线程访问锁时,如果该锁没有被其他线程访问,即ThreadId字段为空,那么JVM让其持有偏向锁,并将ThreadId字段的值设置为该线程的ID。

    轻量锁

    多个线程在不同时段获取同⼀把锁,即不存在锁竞争的情况,也就没有线程阻塞。针对这种情况,JVM采⽤轻量级锁来避免线程的阻塞与唤醒。

    重量锁

    当竞争激烈时,不会立刻进入阻塞状态而是会自旋一段时间看是否能获取锁如果不能则进入阻塞状态。

    各种锁之间的优缺点比较

    在这里插入图片描述


    CAS

    CAS简介

    CAS的全称是:⽐较并交换(Compare And Swap)。在CAS中,有这样三个值:

    • V:要更新的变量(var)
    • E:预期值(expected)
    • N:新值(new)

    比较并交换的过程如下:

    判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。

    所以这⾥的预期值E本质上指的是“旧值”。

    我们以⼀个简单的例⼦来解释这个过程:

    1. 如果有⼀个多个线程共享的变量 i 原本等于5,我现在在线程A中,想把它设置为新的值6;
    2. 我们使⽤CAS来做这个事情;
    3. ⾸先我们⽤i去与5对⽐,发现它等于5,说明没有被其它线程改过,那我就把它设置为新的值6,此次CAS成功, i 的值被设置成了6;
    4. 如果不等于5,说明 i 被其它线程改过了(⽐如现在 i 的值为2),那么我就什么也不做,此次CAS失败, i 的值仍然为2。

    在这个例⼦中, i 就是V,5就是E,6就是N。

    当多个线程同时使⽤CAS操作⼀个变量时,只有⼀个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。


    AQS

    AQS简介

    AQS是 AbstractQueuedSynchronizer 的简称,即 抽象队列同步器 ,从字⾯意思上理解:

    • 抽象:抽象类,只实现⼀些主要逻辑,有些⽅法由⼦类实现;
    • 队列:使⽤先进先出(FIFO)队列存储数据;
    • 同步:实现了同步的功能。

    AQS是⼀个⽤来构建锁和同步器的框架,使⽤AQS能简单且⾼效地构造出应⽤⼴泛的同步器,⽐如我们提到的ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。

    展开全文
  • 多线程原理和理解 (集合了一波别人的专业术语和自己的代码) 什么是多线程: · 在一个程序中,一些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“...

    关于多线程的理解和原理(集合了一波别人的专业术语和自己的代码)

    什么是多线程:

    · 在一个程序中,一些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。

    多线程的基础原理

    · 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

    线程

    · 每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组 指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。
    · 线程是程序中一个单一的顺序控制流程.在单个程序中同时运行多个线程完成不同的工作,称为多线程.
    · 线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文.多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定. 线程的运行中需要使用计算机的内存资源和CPU。

    多线程有什么好处

    · 使用线程可以把占据时间长的程序中的任务放到后台去处理
    · 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
    · 程序的运行速度可能加快
    · 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等等。
    . 多线程技术在IOS软件开发中也有举足轻重的位置。
    . 线程应用的好处还有很多,就不一一说明了

    多线程有什么坏处

    · 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
    · 更多的线程需要更多的内存空间。
    · 线程可能会给程序带来更多“bug”,因此要小心使用。
    · 线程的中止需要考虑其对程序运行的影响。
    · 通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。

    测试多线程中常见的代码及意义

    /**测试Thread中的常用方法
     * 1.Start():启动当前线程,调用当前线程的run()
     * 2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明再次方法中
     * 3.currentThread():静态方法,返回当前代码执行的线程
     * 4.getName();获取当前线程的名字
     * 5.setName();设置当前线程的名字
     * 6.yield();释放当前cpu的执行权
     * 7.join();在线程a中调用线程B的join(),此时线程a就进入阻塞状态。直到线程b完全执行完,才结束阻塞状态
     * 8.stop();强制线程生命期结束,已过时!
     * 9.sleep(Long ,millitime);使线程根据规定的时间来运行
     *
     **/
    

    附赠一下IDEA所需依赖

    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.6.RELEASE</version>
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    
        <repositories>
            <repository>
                <id>central</id>
                <url>http://host:port/content/groups/public</url>
            </repository>
        </repositories>
    
        <pluginRepositories>
            <pluginRepository>
                <id>central</id>
                <url>http://host:port/content/groups/public</url>
            </pluginRepository>
        </pluginRepositories>
    

    以下代码简单调用一下上面的方法及如何去使用方法

    多线程测试创建MyThread类继承于Thread

    /**多线程的创建方式一:继承与Thread类
     * 1.创建一个继承与Thread的子类
     * 2.重写Thread类的run()  -->此线程执行的操作声明在Run()中
     * 3.创建Thread类的子类的对象
     * 4.通过此对象调用start()
     *
     * 例子“遍历100以内的所有偶数
     *
     *
     */
    class MyThread extends Thread{
    //重写Runf()
        @Override
        public void run() {
            for (int i = 0;i < 100;i++){
                if (i % 2 == 0) {
                    System.out.println(Thread.currentThread().getName()+";"+i);
                }
            }
        }
    }
    public class ThreadTest{
        public static void main(String[] args) {
            //创建thread的子类对象
            MyThread mt = new MyThread();
            mt.start();//1启动当前线程,2调用当前线程的run()
            //下面for是在另一个线程启动执行
            for (int i = 0;i < 100;i++) {
                if (i % 2 == 0) {
                    System.out.println(Thread.currentThread().getName()+";"+i);
                }//问题:我们不能通过直接调用run()的方式启动线程
                 //问题: 在启动一个县城,遍历100以内的偶数,不可以还让已经Start()的线程去执行
                    //需要从新创建一个线程的对象,创建多个线程就要创建多个对象
            }
        }
    }
    

    创建ThreadDemo类

    //创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数
    public class ThreadDemo {
        public static void main(String[] args) {
    //        Thread1 t1 = new Thread1();
    //        Thread2 t2 = new Thread2();
    //        t1.start();
    //        t2.start();
            //创建Thread类的匿名子类的方式
            new Thread(){
                @Override
                public  void run(){
                    for (int i = 0;i<100;i++){
                        if (i % 2 == 0){
                            System.out.println(Thread.currentThread().getName()+";"+i);
                        }
                    }
                }
            }.start();
            new Thread(){
                @Override
                public  void run(){
                    for (int i = 0;i<100;i++){
                        if (i % 2 != 0){
                            System.out.println(Thread.currentThread().getName()+";"+i);
                        }
                    }
                }
            }.start();
        }
    
    }
    //class  Thread1 extends Thread{
    //    @Override
    //    public  void run(){
    //        for (int i = 0;i<100;i++){
    //            if (i % 2 == 0){
    //                System.out.println(Thread.currentThread().getName()+";"+i);
    //            }
    //        }
    //    }
    //}
    //class  Thread2 extends Thread{
    //    @Override
    //    public  void run(){
    //        for (int i = 0;i<100;i++){
    //            if (i % 2 != 0){
    //                System.out.println(Thread.currentThread().getName()+";"+i);
    //            }
    //        }
    //    }
    //}
    

    创建方法类HelloThread继承于Thread

    class HelloThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (i % 2 == 0) {
    
                    try {
                        sleep(1000);//括号内直接出入数字
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    System.out.println(Thread.currentThread().getName() + ";" + i);
                }
    //            if (i % 20 == 0){
    //                this.yield();
    //            }
            }
        }
    //    public  HelloThread1(String name){
    //        super(name);
    //    }
    }
    public class ThreadMethodTest {
        public static void main(String[] args) {
    //        HelloThread5 h5 = new HelloThread5(name + "Thread:1");
            HelloThread h1 = new HelloThread();
            h1.setName("线程1");
            h1.start();
            //给主线程命名
            Thread.currentThread().setName("主线程");
            for (int i = 0; i < 100; i++) {
                if (i % 2 != 0) {
                    System.out.println(Thread.currentThread().getName() + ";" + i);
                }
                if (i == 20){
                    try {
                        h1.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    展开全文
  • java多线程实现原理

    2019-08-13 17:37:33
    线程会读写工作内存,CPU会周期性的将工作数据刷入主存,如果线程写工作内存,就会导致每个线程的工作内存、主存内存数据都不一致,最终导致执行结果无法预期。 线程1 —|工作内存|—> [ ] [ 主存 ] 线程2 ...

    java内存模型

    java的内存模式 线程 - 工作内存 - 主存。线程会读写工作内存,CPU会周期性的将工作数据刷入主存,如果多个线程写工作内存,就会导致每个线程的工作内存、主存内存数据都不一致,最终导致执行结果无法预期。

    线程1 —|工作内存|—>  [      ]
                       [ 主存 ]
    线程2 —|工作内存|—>  [      ]
    

    happens-before规则

    由于编译器,CPU,内存会出现指令重排序。其中一个数据的读写指令的重排序,会对执行结果产生变化,对于这种存在数据依赖的指令是不允许重排序的。happens-before是java对程序员的一种保证,它的内容是:

    1. 对于单个线程,之前的操作一定happens-before之后的操作
    2. 对于synchronized关键字,加锁的命令一定在解锁执行
    3. 对于volatile对象,写一定在读之前执行
    4. happens-before具有延续性,即a happens-before b,b happens-before c,那么a happens-before

    happens-before并不是要求处理器一定要顺序一致性的执行指令,而是保证重排序的执行对最终执行结果没有影响。

    synchronized

    synchronized是锁,悲观锁。它的原理是,它锁住的对象会放入到monitor中,monitor只允许一个线程进入,synchronized括住的代码就是要进入monitor获得对象的代码。

    synchronized(lock) {
      a(lock);
      b(lock);
    }
    

    wait/notify模式

    所有的Object都有wait/notify方法,wait可以让当前线程进入等待状态,notify可以让等待线程恢复运行状态。wait/notify必须在synchronized语句块中运行,而且必须是锁着的那个对象执行wait/notify方法。

    使用wait/notify可以实现线程间的通信。

    wait/notify经典范式

    synchronized(lock) {
      while(条件不满足)
      	lock.wait();
      逻辑运行
    }
    
    synchronized(lock) {
    	改变条件
      lock.notify();
    }
    

    volatile语义

    volatile保证了对象在多线程的可见性,即volatile对象多线程看到的,都是一致的。它在于java模型中的语意是,volatile对象写入工作内存后,会立刻刷入主存;而volatile的读,会废弃工作内存内的,直接读主存的数据。volatile简单的读写是原子性的。

    AQS

    AQS(AbstractQueueSynchronized)是JUC的的核心实现类。它采用模版方法模式,实现获取独占锁获取资源,释放资源,共享锁获取资源,共享锁释放资源四个模版方法,实现类需要实现其获得资源和释放资源的自己实现。

    AQS有一个同步队列,用于存放等待的线程节点,AQS有队列的head和tail对象。

    获取资源的流程,获取资源,获取失败新建等待节点,放入同步队列,不断自旋判断前驱是first并是否能获得资源,将当前节点设为头节点,则返回true,不能返回false;释放独占锁资源是尝试释放资源,成功唤醒下一个同步队列的线程。共享锁获取资源和释放资源与独占锁相似,只是独占锁获取资源方法返回true/false,而共享锁返回int,当放回值为0时,共享锁释放成功。

    AQS的逻辑实现

    public final void acquire(int arg) {
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
    

    JUC中的锁

    JUC中的锁是指Lock接口,lock和unlock是加锁,解锁的方法。Lock接口的实现类有重入锁,读写锁。

    重入锁是一个独占锁,它通过两个继承AQS的同步器来实现加锁,解锁操作。重入锁有两个同步器,一个是非公平同步器,一个是公平同步器,默认采用非公平同步器。公平同步器继承AQS类,它实现tryAcquire和tryRelease方法,tryAcquire的实现逻辑是当state为0时,当前线程是同步队列头节点对应线程且cas修改state为1,成功将holdThread改为当前线程;当state不为0,判断holdThread是否是当前线程,如果是state=state+1,如果不是返回false。非公平锁和公平锁逻辑相似,只是没有判断是否是头节点这个判断。释放资源相似,都是state=state-1。非公平锁造成一个线程释放资源之后又获取资源,减少线程切换次数。

    读写锁有两个锁对象,一个读锁,一个写锁,它们公用一个同步器,其中写锁用的是其独占锁的资源获取和释放方法,读锁用的是其共享锁的资源获取和释放方法。独占锁和共享锁通过将32位的state的前16位用于记录读锁资源,后16位用于记录写锁资源。

    condition的实现

    condition是AQS的等待队列,它通过await新建线程节点放入等待队列,并让线程阻塞;signal唤醒等待队列中的节点线程。它相当于实现了wait/notify的方法。

    一个condition对象就是一个新的等待队列,它记录了头,尾节点,它和同步队列公用一个Node类。

    阻塞队列

    BlockQueue接口有linkedBlockQueue,ArrayBlockQueue等实现类,它们有通过锁的加,解锁实现。

    还是有ConcurrentLinkedQueue,它是通过循环+CAS+volatile头尾节点实现,它对volatile头尾节点的写有个优化,不是每次都写,而是隔一次再写,如入队,第一次只更新next到新节点,第二次才更新tail节点;出对第一次将head的item赋null,第二次才更新头节点。

    同步工具

    同步工具有Fork/Join框架,等待多线程完成框架CountDownLatch,线程屏障框架CyclicBarrier,两个线程交换数据的Exchanger框架。

    Fork/Join框架是一个自动分解任务,放入任务队列,同时线程池执行任务的一个框架。使用需要继承fork/join的任务类,并在实现方法中实现任务分割的规则,分割的任务调用fork方法时,将任务放入任务队列并安排工作线程执行,调用join时,阻塞线程到结果返回。ForkJoinPool就是一个继承类的实现类。

    CountDownLatch框架实现的是原先的join功能,它的作用是规定多少个线程执行,主线程调用await阻塞,等规定数量的线程执行后,主线程唤醒。它通过同步器实现,await就是获得共享锁,当state==0时,就可以获得锁。

    CycliBarrier是线程屏障,当设定数量的线程都达到线程屏障时,所有线程能才能执行。它使用重入锁和condidion实现,当调用await时,获得独占锁,state–,当state!=0时,放入等待队列;==0时,signalAll恢复所有等待线程。

    线程池Executor

    executor通过execute执行任务。它主要的实现类是TheadPoolExecutor。通常使用ExecutorService创建线程池,它提供了三个模版创建,fixTheadPool,singleTheadPool,cacheTheadPool。

    实现是一个线程set,一个阻塞任务队列。执行execute时,判断当前线程数是否小于coreSize,如果小于新建核心工作线程,把任务给它执行;当大于coreSize时,将任务放入阻塞队列;当阻塞队列放不下时,判断线程数是否小于maxSize,如果小于,新建非核心工作线程,把任务给它执行;如果线程数大于maxSize,执行饱和策略。

    工作线程Work的run方法逻辑是,循环取任务,执行任务.run。如果task==null,跳出循环。获取任务的方法会死循环获得任务队列任务,只有在任务队列为空,且线程数大于核心线程数,且超时的情况下,返回null,结束运行,可以设置允许核心线程timeout。

    我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=60ah6d2uh855

    展开全文
  • 多线程的计算机原理

    2019-08-13 11:13:14
    在操作系统的帮助下,cpu时间被切分为一个一个的时间片。这些时间片分别分配给不同的线程线程得到时间片之后,...因为时间片被切分的很小,所以在人类看起来,线程好像是在同时进行的。 原理图手绘版如下: ...

    在操作系统的帮助下,cpu时间被切分为一个一个的时间片。这些时间片分别分配给不同的线程,线程得到时间片之后,在这个时间片范围内可以使用cpu资源。因为时间片被切分的很小,所以在人类看起来,多个线程好像是在同时进行的。

    原理图手绘版如下:
    在这里插入图片描述

    展开全文
  • Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用_clone()和fork(),最终都用不同的参数调用do_fork()核内API。 do_fork() 提供了很参数,包括CLONE_VM(共享内存...
  • 一个应用程序就是一个进程,一个进程可以有线程。。。。。。 你要是这样回答,遇到一个菜逼面试官可能就混过去了,但是遇到一个资深的大佬,你可能就栽在这个问题上了(哈哈) 那我们现在就从深层次的剖析这个...
  • 多线程实验

    千次阅读 2018-07-26 20:19:03
    实验六 多线程实验 一、实验目的 练习多线程类的两种实现方法,理解多线程程序的生命周期 二、实验内容 编写一多线程程序,实现如下功能: (1)一个线程进行阶乘和的运算(1!+2!+3!+……+12!),每次阶乘...
  • java多线程实验

    2013-06-20 13:36:55
    java设计两个线程,一个线程充当电子表,每隔一秒在窗口上显示一下系统时间;另一个线程充当闹钟,每到整点就开始报时,即在窗口显示5次整点提示,同时将第一个线程挂起,报时完毕后再将第一个...实验结果如图1-1所示:
  • 【java】--多线程原理

    2015-08-05 22:41:15
    自考在《操作系统中》学到了关于进程和线程的概念,结合Demo更好的理解了多线程原理, 这篇博客主要讲一下多线程的创建和我对这个创建过程的理解。 首先展现一张思维导图: 进程:是一个正在执行中的程序。 ...
  • 简单的说,当程序同时完成多件事情时,就是所谓的多线程程序,比如说一个人可以同时进行呼吸和思考。同时做多件事情这种机制在Java中被称为并发,而将并发完成的每一件事情称为线程。可能这样解释有点牵强,我们结合...
  • 多线程的实现原理

    2018-09-05 01:37:00
    在java中提供了一系列和并发处理相关的关键字,比如volatile、synchronized、final、juc等,这些就是java内存模型封装了底层的实现后提供给开发人员使用的关键字,在开发多线程代码的时候,我们可以直接使用...
  • 多线程同步的原理

    千次阅读 2016-03-12 22:19:26
    多线程执行 进程在活动中会相互制约 所有进程都是相互独立的 进程之间以异步并发方式执行 同步 同步是进程共同完成一项任务时互相发生的关系 同步进程之间具有合作关系 执行的时候必须按照一定顺序协调进行 互斥...
  • 多线程的基本原理

    2019-08-14 13:40:03
    多线程对于共享变量访问带来的安全性问题 一个变量 i. 假如一个线程去访问这个变量进行修改,这个时候对于数据的修改和访问没有任何问题。但是如果多个线程对于这同一个变量进行修改,就会存在一个数据安全性问题 ...
  • 多进程与多线程的实现与原理

    千次阅读 2018-02-12 01:34:34
    多进程 ...使用多线程实现tcp并发 开启线程的俩种方式 进程和线程的俩种区别 区别一:启动数据快 区别二:线程间资源共享,进程间资源独立 守护线程的使用 线程的互斥锁 线程的GIL锁(解释器锁) pa...
  • C#关于多线程原理

    2013-06-20 01:42:33
    相信大家经常听说 进程和线程 ,当然这东西出现必然有他的用处 什么是进程? 图书上就是进程,你会发现我的电脑运行个进程   对于以前的单核电脑来说(它在某一时刻只能执行一个进程,为什么我们既能听歌,又...
  • 原创的嵌入式多线程实验报告原创,情歌问大佬多多发表意见,新人初到,以后的博客会越来越有技术含量,文件会越来越有帮助
  • 多线程的目的是为了最大限度的利用CPU资源。  同一进程的里多个子线程看似可以同步进行,其实不是同步的(由于CPU在同一时间内只能处理一个线程),他们可以访问父线程的资源与对象,这样一来的优点是可以实现子...
  • 然后再在Tomcat的webapps/root目录下放一个.exe文件,来试验多线程下载。 创建一个java工程: Demo代码: import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnecti
  • 为什么我们需要使用多线程 提高用户体验 避免ANR(Application is not responding)
  • 多线程实验

    2018-10-31 17:35:00
    public static void main(String[]args){ TestThread testThread = new TestThread(); TestThread2 testThread2 = new TestThread2(); testThread.start();//程序执行此处时,并没阻塞,而是继续往下执行 ...
  • 用户态多线程实现的基本原理

    千次阅读 2009-12-05 14:12:00
    本文参考了用户态非抢占式线程库实现... Keywords: User-Space MultiThreading, Pth 所谓多线程,简单讲就是能够让几个不同的代码片段轮流执行。内核实现多线程的方法比较直观,在每次时钟中断到来时或者用户调用s
  • 多线程

    2020-06-17 21:28:10
    ThreadLocal 是什么?有哪些使用场景? 线程局部变量是局限于线程内部的变量,属于线程自身所有,不在线程间共享。Java 提供 ThreadLocal 类来支持...synchronized 底层实验原理? synchronized 可以保证方法或者
  • Java 多线程 学习笔记 2020/5/13 文章目录Java 多线程 学习笔记一. 线程与进程二. Java中创建线程1.编写继承Thread的类,重写run方法2. 编写实现Runnable的类,重写run方法3. 其他方式的创建三. 线程的生命周期四. ...
  • 多线程 之 Lock 锁的实现原理

    千次阅读 2017-04-05 16:44:52
    1. Lock 的简介及使用 Lock完全用Java写成,在java这个层面是无关JVM实现的,Lock ...//尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常 void lockInterruptibly() throws InterruptedExc
  • 要说Java的多线程,首先要明白什么是多线程多线程,线程是 程序 中一个单一的顺序控制流程.在单个程序中同时运行多个线程完成不同的工作,称为多线程. 我勒个擦,定义好官方啊,对于那些语文课时体育...
  • 使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多。如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机中并非并发执行,而是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,170
精华内容 16,068
关键字:

多线程实验原理