鸽了好久以后终于又更新了,看同学去实习都是先学源码然后修改之类,才发觉只是知道语法怎么用还远远不够,必须要深入理解以后不管是学习还是工作,才能举一反三,快速掌握。

基础知识

  既然说到了多线程,那自然就要了解一下线程到底是什么,计算机又是怎么实现对应功能的呢。

进程与线程

进程: 是代码在数据集合的一次运行活动,是系统进行资源分配和调度的基本单位。我们常用的程序就是一个进程,像qq音乐、word之类,进程就是一个程序的动态执行过程。

线程: 线程是进程的一个组成部分,一个进程可以有多个线程,而一个线程只能属于一个进程。线程可以拥有自己的堆栈、自己的程序计数器还有自己的局部变量,但不能拥有系统资源。它与父进程的其他线程共享该进程的所有资源。就好比在使用word的时候,可以一遍打字,word自动统计字数、提示语法错误等,这些功能都属于一个线程。

   但其实我们电脑中存在的线程数,要远远大于电脑cpu的个数,如果是单cpu的电脑的话,每一时刻就只有一个线程可以执行,但我们仍然可以实现很多程序的同时使用,其实我们所看到的多线程的运行,只是电脑在极短时间里交替执行多个线程所表现出来的效果,比如我在一边使用word,还一边听歌,那电脑可以每隔1ms播放一下音乐,再接受一下输入,再检测一下字数等,这样以极快的速度处理每个线程的部分代码,从而使得多个任务看起来可以"同时"进行。

   多线程的确可以为我们处理任务提供很大的便利,提高效率,但是因为多线程的机制使得很多在单线程中正常执行的程序出现问题,其中包括:1、原子性问题 2、线程共享内存不可见的问题。

线程原子性问题

   此问题经常出现在多个线程对同一组共享变量的访问中,假设有三个线程,每个线程都会使共享变量x减少1并打印,那么应该有如下示意图:

   

但因为执行的操作 x -= 1 实际上并不是一个操作,执行过程是获得现有x,创建一个新的变量x',然后另x'的数值变为x - 1,之后再将公共变量中x的指针指向新的变量x',从而实现对应操作。这样的非原子性的操作就会出现这样的情况:当A线程在获得了x,并且执行x-1以后,还没有将公共变量指针指向新的变量x'的时候,线程B进入并且也获得了x,此时线程B获得的x就还是没有更新的x,从而造成了数据读写不一致的问题。

线程共享内存不可见问题

   出现不可见性问题的原因与线程访问共享数据方式有关,每一个线程都有单独的堆栈、程序计数器等信息,当线程要读取公共变量的时候,也是先读取到自己的栈中,然后进行操作,最后写回到进程的公共变量里去,但若在线程A将变量加入自己的栈后,线程B将修改后的结果写回公共变量,但此时线程A不知道公共变量已经修改,从而造成不一致问题。

   基于这些问题,我们可以使用synchronized 以及volatile 关键字来进行避免,这些将在下一篇单独讲解。

线程状态介绍

   线程共有五种状态,分别是新建状态(NEW),可运行状态(RUNNABLE), 运行状态(RUNNING),阻塞状态(BLOCKED),死亡状态(DEAD).现对其一一介绍:

新建状态

   新建状态(NEW):指新创建了一个线程对象。

可运行状态

   可运行状态(RUNNABLE):指有资格可以运行,但是没有获得cpu,所以要等待直到获得cpu后变成运行状态(RUNNING)。

   有很多方法可以使线程变成可运行状态,譬如:

   1、对新建立的线程执行start()方法。

   2、对线程调用sleep()方法使之变为阻塞状态,当sleep时间过后,会恢复成可运行状态。

   3、在本线程使用join()方法,当指定线程执行完毕后,本线程恢复为可运行状态。

   4、使用yield()方法,进入可运行状态。

运行状态

   运行状态就是线程获得了cpu,并执行对应代码。

阻塞状态

   阻塞状态是程序让出cpu控制权,并且暂时停止运行。直到指定条件达成以后,有资格运行,变成可运行状态。

   使程序进入阻塞状态的方法有:

   1、等待阻塞:使用wait()方法,直到被notify()方法唤醒

   2、同步阻塞:申请的资源上锁并被其他资源占用,此时阻塞直到对象锁被释放

   3、其他阻塞:比如使用join()方法,则当前线程会阻塞直到指定线程完成。使用sleep()等待进入阻塞状态,直到等待时间后恢复。等待io处理等。

死亡状态

   一般是因异常退出了run(),或者程序执行完成

多线程使用方法

   如果只是单纯地使用多线程而不是深究其同步问题的话,java提供的Thread类是已经足够使用的,而对于同步问题的解决,将会在下一篇中进行详细分析与介绍。

线程的创建

   可以使用两种方法来创建线程,分别是继承Thread并重写run()方法,或者实现Runable接口的run()方法,因为java不支持多继承,因此推荐使用实现接口的方式.

可以采用接口实现:

    //使用接口实现
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println("run!");
}
}

也可以使用继承的方式实现:

public class MyThread02 extends Thread {
@Override
public void run() {
System.out.println("i am running!");
}
}

线程的运行

   调用start()方法即可运行。虽然我们写的是run()方法,但不要直接调用run()方法,那样就起不到多线程的作用了,应该调用start()方法,然后会将该线程变为可运行状态,根据启动条件和cpu情况,适时地执行程序。

线程的礼让

   使用yield()方法,使线程让出cpu,重新回到可运行状态。这时候会继续争夺cpu,也就是有可能线程A使用yield()方法让出cpu以后,又重新获得cpu的使用资格。也就是如果有A、B两个线程,并不一定A线程礼让的时候,B线程一定先于A线程执行。

线程的睡眠

   使用sleep()方法,使线程阻塞一定时间。

线程的join

   使用threadx.join()方法,使得当前线程堵塞,直到threadx线程执行完成。

线程的等待与唤醒

   一般都与锁一起使用。

java 多线程详细笔记(原理理解到全部使用)的更多相关文章

  1. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  2. Java多线程学习笔记--生产消费者模式

    实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...

  3. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  4. Java多线程学习笔记(一)——多线程实现和安全问题

    1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...

  5. Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解

       我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于 ...

  6. Java 多线程学习笔记:生产者消费者问题

    前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章<阻塞队列实现生产者消费者模式>.在文中,使用的是Java的concurrent包中的阻塞队列来实现.在看完后 ...

  7. java多线程-读写锁原理

    Java5 在 java.util.concurrent 包中已经包含了读写锁.尽管如此,我们还是应该了解其实现背后的原理. 读/写锁的 Java 实现(Read / Write Lock Java ...

  8. java多线程断点下载原理(代码实例演示)

    原文:http://www.open-open.com/lib/view/open1423214229232.html 其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载 ...

  9. java多线程学习笔记(三)

    java多线程下的对象及变量的并发访问 上一节讲到,并发访问的时候,因为是多线程,变量如果不加锁的话,会出现“脏读”的现象,这个时候需要“临界区”的出现去解决多线程的安全的并发访问.(这个“脏读”的现 ...

随机推荐

  1. Jmeter系列(7)- 基础线程组Thread Group

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html Thread Group基础线程组介绍 ...

  2. Jest 前端单元测试工具

    Jest和enzyme 前端单元测试工具 什么是Jest? Jest是一个令人愉悦的JavaScript测试框架,其重点是简单性. 它适用于使用以下项目的项目:Babel,TypeScript,Nod ...

  3. MAC地址欺骗(原理及实验)

    MAC地址欺骗 MAC地址欺骗(或MAC地址盗用)通常用于突破基于MAC地址的局域网访问控制,例如在交换机上限定只转发源MAC地址修改为某个存在于访问列表中的MAC地址即可突破该访问限制,而且这种修改 ...

  4. libeay32.dll 1.0.2j crash

    https://github.com/BOINC/boinc/issues/2470 他们认为是CPU不同造成的 另外一个可能的原因 Changes between 1.0.2j and 1.0.2k ...

  5. 理解分布式一致性:Paxos协议之Generalized Paxos & Byzantine Paxos

    理解分布式一致性:Paxos协议之Generalized Paxos & Byzantine Paxos Generalized Paxos Byzantine Paxos Byzantine ...

  6. 二进制安装MySQL及破解密码

    二进制安装MySQL及破解密码 1.确保系统中有依赖的libaio 软件,如果没有: yum -y install libaio 2.解压二进制MySQL软件包 tar xf mysql-5.7.24 ...

  7. unix域源码解析

    首先我们先要创建一个用于通信的结构unix_proto_data ,并初始化某些字段 static int unix_proto_create(struct socket *sock, int pro ...

  8. spark系列-4、spark序列化方案、GC对spark性能的影响

    一.spark的序列化 1.1.官网解释 http://spark.apache.org/docs/2.1.1/tuning.html#data-serialization 序列化在任何分布式应用程序 ...

  9. DFS--POJ 1190 生日蛋糕

    Description 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri ...

  10. 一张图告诉你UML图怎么画❀

    UML 能帮我们做什么? 我们在进行项目的时候,通过使用 UML 的面向对象图的方式来更明确.清晰的表达项目中的架设思想.项目结构.执行顺序等一些逻辑思维. UML 介绍: 1997年,OMG 组织( ...