多线程1,线程基础知识

多线程基础知识

目录介绍

  • 1.进程概述及多进程的意义[理解]
    • 1.1 线程和进程
    • 1.2 进程概述
    • 1.3 多进程的意义
  • 2.线程的概述和多线程的意义[理解]
    • 2.1 什么是线程
    • 2.2 多线程有什么意义
    • 2.3 并行和并发
  • 3.JVM运行原理以及JVM启动的线程探讨[理解]
    • 3.1 Java程序运行原理
    • 3.2 JVM的启动是多线程的吗
  • 4.多线程程序实现的方式[掌握]
    • 4.1 多线程程序实现的方式【重点】
    • 4.1.1 第一种方式:是类继承Thread
    • 4.1.2 第二种方式:是实现接口Runnable
    • 4.2 多线程两种方式的区别
    • 4.3 几个小问题探索
    • 4.4 匿名内部类的方式实现多线程程序
  • 5.线程调度
    • 5.1 线程的调度问题
    • 5.2 线程有两种调度模型
  • 6.线程控制
    • 6.1 线程控制之休眠线程
    • 6.2 线程控制之加入线程
    • 6.3 线程控制之礼让线程
    • 6.4 线程控制之守护线程
    • 6.5 线程控制之中断线程
  • 7.案例分析
    • 7.1 继承Thread类的方式卖电影票案例
    • 7.2 实现Runnable接口的方式卖电影票
    • 7.3 买电影票出现了同票和负数票的原因分析
    • 7.4 线程安全问题的产生原因分析
    • 7.5 同步代码块的方式解决线程安全问题

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

0.前沿介绍

1.进程概述及多进程的意义[理解]

1.1 线程和进程

  • 要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。

1.2 进程概述

  • 什么是进程呢?通过任务管理器我们就可以看到进程的存在。
  • 概念:进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

1.3 多进程的意义

  • 单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
  • 对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
  • 因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
  • 所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速度,而是提高CPU的使用率。

2.线程的概述和多线程的意义[理解]

2.1 什么是线程

  • 在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。

2.2 多线程有什么意义

  • 多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
  • 那么怎么理解这个问题呢?我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.

2.3 并行和并发

  • 前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
  • 后者是物理上同时发生,指在某一个时间点同时运行多个程序。

3.JVM运行原理以及JVM启动的线程探讨[理解]

3.1 Java程序运行原理

  • Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
  • 该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。

3.2 JVM的启动是多线程的吗

  • JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

4.多线程程序实现的方式[掌握]

4.1 多线程程序实现的方式

  • 4.1.1 第一种方式:是类继承Thread
第一种方式的步骤:
1: 定义一个类,让该类去继承Thread类
2: 重写run方法
3: 创建该类的对象
4: 启动线程

public class ThreadDemo {
    public static void main(String[] args) {
        // 创建对象
        MyThread t1 = new MyThread() ;
        MyThread t2 = new MyThread() ;
        // 启动线程: 需要使用start方法启动线程, 如果我们在这里调用的是run方法,那么我们只是把该方法作为普通方法进行执行
//      t1.run() ;
//      t1.run() ;
        t1.start() ;        // 告诉jvm开启一个线程调用run方法
        // t1.start() ;     // 一个线程只能被启动一次
        t2.start() ;
        
    }
}

public class MyThread extends Thread {
    @Override
    public void run() {
        for(int x = 0 ; x < 1000 ; x++) {
            System.out.println(x);
        }
    }
}
  • 4.1.2 第二种方式:是实现接口Runnable
实现多线程的第二中方式步骤:
1: 定义一个类,让该类去实现Runnable接口
2: 重写run方法
3: 创建定义的类的对象
4: 创建Thread的对象吧第三步创建的对象作为参数传递进来
5: 启动线程

public static void main(String[] args) {
    // 创建定义的类的对象
    MyThread mt = new MyThread() ;
    // 创建Thread的对象吧第三步创建的对象作为参数传递进来
    Thread t1 = new Thread(mt , "张三") ;
    Thread t2 = new Thread(mt , "李四") ;
    // 启动线程
    t1.start() ;
    t2.start() ;
}

public class MyThread implements Runnable {
    @Override
    public void run() {
        for(int x = 0 ; x < 1000 ; x++) {
            System.out.println(Thread.currentThread().getName() + "---" + x);
        }
        
    }
}

4.2 多线程两种方式的区别

  • run()方法只是调用了Thread实例的run()方法而已,它仍然运行在主线程上,而start()方法会开辟一个新的线程,在新的线程上调用run()方法,此时它运行在新的线程上。

4.3 几个小问题探索

  • 4.3.1 为什么要重写run方法
  • 可以在定义的类中,定义多个方法,而方法中的代码并不是所有的都需要线程来进行执行; 如果我们想让某一个段代码被线程,那么我们只需要将那一段代码放在run方法中。那么也就是说run方法中封装的都是要被线程执行的代码 ;
  • run方法中的代码的特点: 封装的都是一些比较耗时的代码
  • 4.3.2 线程能不能多次启动
  • 一个线程只能被启动一次
  • 4.3.2 run()和start()方法的区别
  • 启动线程: 需要使用start方法启动线程, 如果我们在这里调用的是run方法,那么我们只是把该方法作为普通方法进行执行。

4.4 匿名内部类的方式实现多线程程序

  • new Thread(){代码…}.start();
  • new Thread(new Runnable(){代码…}).start();

5.线程调度

5.1 线程的调度问题

  • 应用程序在执行的时候都需要依赖于线程去抢占CPU的时间片 , 谁抢占到了CPU的时间片,那么CPU就会执行谁
  • 线程的执行:假如我们的计算机只有一个 CPU,那么 CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。

5.2 线程有两种调度模型

  • 5.2.1 分时调度模型
  • 所有线程轮流使用CPU的使用权,平均分配每个线程占用 CPU 的时间片
  • 5.2.2 抢占式调度模型
  • 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。Java使用的是抢占式调度模型。

6.线程控制

6.1 线程控制之休眠线程

  • public static void sleep(long time) ;
  • time表达的意思是休眠的时间 , 单位是毫秒

6.2 线程控制之加入线程

  • public final void join()
  • 等待该线程执行完毕了以后,其他线程才能再次执行
  • 注意事项: 在线程启动之后,在调用方法

6.3 线程控制之礼让线程

  • public static void yield():
  • 暂停当前正在执行的线程对象,并执行其他线程。
  • 线程礼让的原理是: 暂定当前的线程,然CPU去执行其他的线程, 这个暂定的时间是相当短暂的; 当我某一个线程暂定完毕以后,其他的线程还没有抢占到cpu的执行权 ; 那么这个是时候当前的线程会和其他的线程再次抢占cpu的执行权;

6.4 线程控制之守护线程

  • public final void setDaemon(boolean on)
  • 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。
  • jvm会线程程序中存在的线程类型,如果线程全部是守护线程,那么jvm就停止。

6.5 线程控制之中断线程

  • public final void stop():
  • 停止线程的运行
  • public void interrupt():
  • 中断线程(这个翻译不太好),查看API可得当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞

7.案例分析

7.1 继承Thread类的方式卖电影票案例

public class ThreadDemo {
    public static void main(String[] args) {
        /**
         * 需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
         */
        // 创建3个线程对象
        SellTicktes t1 = new SellTicktes() ;
        SellTicktes t2 = new SellTicktes() ;
        SellTicktes t3 = new SellTicktes() ;
        // 设置名称
        t1.setName("窗口1") ;
        t2.setName("窗口2") ;
        t3.setName("窗口3") ;
        // 启动线程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

public class SellTicktes extends Thread {
    private static int num = 100 ;
    @Override
    public void run() {
        /**
         * 定义总票数
         * 
         * 如果我们把票数定义成了局部变量,那么表示的意思是每一个窗口出售了各自的100张票; 而我们的需求是: 总共有100张票
         * 而这100张票要被3个窗口出售; 因此我们就不能把票数定义成局部变量,只能定义成成员变量
         */     
        // 模拟售票
        while(true) {
            if( num > 0 ) {
                System.out.println(Thread.currentThread().getName() + "正在出售" + (num--) + "张票");
            }
        }
    }
}

7.2 实现Runnable接口的方式卖电影票

public class SellTicektesDemo {
    public static void main(String[] args) {
        // 创建SellTicektes对象
        SellTicektes st = new SellTicektes() ;
        // 创建Thread对象
        Thread t1 = new Thread(st , "窗口1") ;
        Thread t2 = new Thread(st , "窗口2") ;
        Thread t3 = new Thread(st , "窗口3") ;
        // 启动线程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

public class SellTicektes implements Runnable {
    private static int num = 100 ;
    @Override
    public void run() {
        while(true) {
            if(num > 0) {
                System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
            }
        }
    }
}

7.3 买电影票出现了同票和负数票的原因分析

  • 讲解过电影院售票程序,从表面上看不出什么问题,但是在真实生活中,售票时网络是不能实时传输的,总是存在延迟的情况,所以,在出售一张票以后,需要一点时间的延迟。改实现接口方式的卖票程序,每次卖票延迟100毫秒
public class ThreadDemo {
    public static void main(String[] args) {
        // 创建3个线程对象
        SellTicktes t1 = new SellTicktes() ;
        SellTicktes t2 = new SellTicktes() ;
        SellTicktes t3 = new SellTicktes() ;
        // 设置名称
        t1.setName("窗口1") ;
        t2.setName("窗口2") ;
        t3.setName("窗口3") ;
        // 启动线程
        t1.start() ;
        t2.start() ;
        t3.start() ;
    }
}

public class SellTicktes extends Thread {
    private static int num = 100 ;
    @Override
    public void run() {
        // 模拟售票
        while(true) {
            if( num > 0 ) {
                try {
                    Thread.sleep(100) ;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在出售" + (num--) + "张票");
            }
        }
    }
}

7.4 线程安全问题的产生原因分析

  • 7.4.1 首先想为什么出现问题?
  • 是否是多线程环境,是否有共享数据,是否有多条语句操作共享数据
  • 7.4.2 如何解决多线程安全问题呢?
  • 基本思想:让程序没有安全问题的环境。怎么实现呢?把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。

7.5 同步代码块的方式解决线程安全问题

  • 7.5.1 同步代码块的格式
  • 同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能
synchronized(对象){
            需要同步的代码;
        }
  • 7.5.2 同步代码块优势和劣势
  • 同步的好处:同步的出现解决了多线程的安全问题。
  • 同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,085评论 0 23
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,690评论 0 11
  • 写在前面的话: 这篇博客是我从这里“转载”的,为什么转载两个字加“”呢?因为这绝不是简单的复制粘贴,我花了五六个小...
    SmartSean阅读 4,709评论 12 45
  • 很多心灵鸡汤都怂恿我们说“要勇敢踏出第一步”,一旦涉足,来自不同的烦恼便纷至沓来。我们需要做好应付准备
    辛小毛阅读 92评论 0 0
  • 2018年4月7日 星期六 晴 假期最后一天,今天上班中。早晨李云哲说还有一篇作文没写,要跟我去公司写...
    云哲云灿妈妈阅读 23评论 0 0