1、什么是线程

  • 单核CPU = 一个车间:一次执行一个进程,如果执行多个程序,会在多个进程中来回切换,执行到进程里面会在多个线程之间来回切换。
  • 多核CPU = 一个工厂:每次可执行多个进程;
  • 进程:一个车间为一个进程(一个运行的程序);进程是一种重量级的资源,系统会分配内存和CPU资源,启动和停止慢,内存相互独立
  • 线程:车间内一个工人为一个线程;
  • 多线程:一个进程包含多个线程;多个线程都可以共享一个进程的内存空间;

1.1、什么是多线程?

  • 多线程是在CPU切换到某个进程之后,会在多个线程之间来回切换,每个线程就会分配到一定的cpu时间,线程是CPU分配时间的单元

1.2、并行和并发

  • 并行:多个cpu同时执行多个线程
  • 并发:一个CPU同时执行多个线程,CPU在线程之间来回切换,让线程都能执行(不是同时执行)

1.3、同步和异步

  • 同步:多个指令是依次执行的,一个指令执行时会阻塞当前线程,其他指令必须要在该指令完成之后执行。
  • 异步:多个线程同时指向自己的命令,一个线程执行完后,给另一个线程通知

2、多线程的应用场景

  • 大型企业级应用都有高并发的特点,因为会大量的用户,比如:淘宝、京东、抖音等。如果服务器是单线程,所有用户必须排队执行,必须为每个用户的每个请求,分配一个独立的线程,完成独立任务,相互不影响。单机版程序(如:大型游戏)需要执行大量的任务:图形渲染、动作控制、网络通信等。需要多线程同时执行上面任务。

3、启动线程的方法

3.1、继承Thread

public class MyThread extends Thread{    @Override    public void run() {        for (int i = 0; i <100 ; i++) {            System.out.println("hello 我是"+Thread.currentThread().getName());        }    }    public static void main(String[] args) {        MyThread myThread = new MyThread();        myThread.start();//一个线程不能调用两次以上的start方法 IllegalThreadStateException    }}

3.2、实现Runnable接口

public class MyRunnable implements Runnable{    @Override    public void run() {        System.out.println("Runnable :"+ Thread.currentThread().getName());    }     public static void main(String[] args) {        MyRunnable myRunnable = new MyRunnable();        Thread thread  = new Thread(myRunnable);        thread.start();        //匿名内部类写法        new Thread(new Runnable() {            @Override            public void run() {                System.out.println("匿名内部类"+Thread.currentThread().getName());            }        }).start();        //lambda表达式写法        new Thread(() ->{            System.out.println("lambda:"+Thread.currentThread().getName());        }).start();     }}

3.3、实现Callable接口(有返回值)

public class MyCallable implements Callable {//需指定返回值类型    @Override    public Long call() throws Exception {        return 1+1L;    }    public static void main(String[] args) {        //创建MyCallable对象        MyCallable myCallable = new MyCallable();        //创建FutureTask对象        FutureTask futureTask = new FutureTask<>(myCallable);        //创建线程对象        Thread thread = new Thread(futureTask);        thread.start();        try {            System.out.println("获得结果:"+futureTask.get());        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }    }}

4、线程的启动和停止

4.1、线程启动

  • 面试题:启动线程是执行start方法还是run方法?
  • start()方法
  • 只有调用了start方法,系统才会创建新的线程,直接调用run方法,会在主线程中执行
  • 注意:不要手动调用start方法,一个start方法只能被一个线程对象调用一次

4.2、线程停止

  • stop方法(强制停止,不能释放锁资源,可能造成死锁等严重问题,禁用)
  • 等待run方法运行完
  • 通过条件中断run的运行

5、线程的睡眠

  • Thread.sleep(毫秒)
  • 线程一旦进入睡眠,会让出CPU
  • 使用场景:执行大量任务时,进行一定睡眠,可以减少CPU消耗

6、后台进程

  • 也叫守护线程,精灵线程
  • 将一个线程设置为后台线程:setDaemon(true)
  • 守护线程的作用是为其他线程提供服务,一旦其他线程停止了,守护线程会自动停止

7、线程的合并

  • 当前线程可以合并其他线程,当其他线程操作执行完,再执行当前线程的操作。
  • 注意:两个线程不能互相合并,否则会出现死锁的问题

8、线程的优先级

  • 默认情况下线程的执行是抢占式的,没有特定的次序,不同的优先级获得CPU的概率不同
  • setPriority(int) 设置优先级,默认是5,可设置范围1~10

9、线程的生命周期

[图片上传失败...(image-f8805d-1600949547611)]

  1. 新建:对象刚被创建出来,还没有调用start
  2. 就绪:调用了start,但没有抢到CPU,不能执行
  3. 运行:抢到了CPU,正在执行run
  4. 阻塞:线程遇到某些情况,暂停执行
  5. 死亡:run方法执行完

10、线程同步问题

10.1、为什么会出现线程同步问题?

  • 线程是抢占式的,一个线程在执行指令的时候,可能被其他线程中断,可能会出现数据不一致的情况
  • 多个线程同时操作一个资源

10.2、线程同步问题的解决方案(上锁机制)

  • 同步方法语法public synchronized 返回类型 方法名(参数){ } 作用是对整个方法上锁,保证代码的原子性,一旦一个线程进入方法后,就会持有锁,其他线程不能进入该方法,等线程执行完,其他线程才能进去。相关面试题:StringBuffer与StringBuilder的区别ArrayList和Vector的区别HashMap和Hashtable的区别相关知识点同步方法锁的对象是this,当前的对象锁机制:上锁后,JVM会启动监视器(Monitor),监视进入代码块或方法体的线程,如果方法体或代码块的锁被某个线程所有,监视器就会拒绝其他线程进入代码块或方法,直到持有锁的线程执行完,释放锁,才会让其他线程进去一旦上锁,程序的性能会有所降低
  • 同步代码块语法public 返回类型 方法名(参数){ ... synchronized(锁对象){ 代码 } ... } 注意任何Java对象(Object)都可以成为锁对象锁对象不能是局部变量一旦线程进入代码块就上锁,持有锁对象,执行完代码块后自动释放锁,抛异常也会释放锁对比同步方法和同步代码块同步方法更简洁同步方法锁粒度更大(粒度越大锁的范围越大),粒度越大性能越差,同步代码块的性能高于同步方法
  • 同步锁在java1.5出现,在java.util.concurrent包中Lock接口,最上层接口子接口:ReadLock、WriteLock、ReadWriteLock常用的实现类:ReentrantLock重入锁创建对象:Lock lock = new RentrantLock()常用方法:lock.lock()上锁、lock.unlock()释放锁语法lock.lock(); try{ 代码 }finally{ lock.unlock(); }

11、单例模式

  • 单例模式主要分位饿汉式和懒汉式,作用是保证一个类只有一个实例对象优点:减少系统资源的消耗符合某些特殊业务的要求
  • 饿汉式一开始就创建对象,不管getInstance()是否被调用,内存资源都会被消耗掉系统中的Runtime属于饿汉式public class Singleton01 { private static Singleton01 instance = new Singleton01(); private Singleton01(){ } private static Singleton01 getInstance(){ return instance; } }
  • 懒汉式一开始不创建对象,getInstance()被调用时候创建对象,内存一开始不消耗存在线程同步问题(需要在getInstance()方法中判断对象是否为null)//普通懒汉式 public class Singleton02 { private static Singleton02 instance ; private Singleton02(){ } private static Singleton02 getInstance(){ if (instance == null){ instance = new Singleton02(); } return instance; } } //双检锁懒汉式 public class Singleton06 { private static Singleton06 instance ; private Singleton06(){ } private static Singleton06 getInstance(){ if (instance == null){ synchronized (Singleton06.class){ if (instance == null){ instance = new Singleton06(); } } } return instance; } }

12、volatile关键字

  • 用于修饰变量,提高线程可见性,是线程同步的轻量级解决方案,能保证可见性,不能保证原子性
  • 可见性:变量的值每个线程都能直接获取到
  • 原子性:代码作为整体运行,不可再分
  • 变量加上volatile关键字后,线程直接从主内存读取值,不是从缓存读取,任意线程修改变量后,其他线程都可以看到
  • 变量没有被volatile修饰

在这里插入图片描述

  • 变量被volatile修饰

13、线程死锁

  • 出现的情况:上锁后没有正常释放锁两个线程都持有对方需要的锁,又需要对方持有的锁,就可能进入相互等待的情况
public class LockTest {    private static Object lock1 = new Object();    private static Object lock2 = new Object();    public static void main(String[] args) {        Thread thread1 = new Thread(() -> {            for (int i = 0; i < 100; i++) {                synchronized (lock1){                    synchronized (lock2){                        System.out.println("thread1------------"+i);                        try {                            Thread.sleep(100);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                }            }        });        Thread thread2 = new Thread(()->{            for (int i = 0; i < 100; i++) {                synchronized (lock2){                    synchronized (lock1){                        System.out.println("thread2------------"+i);                    }                }            }        });        thread1.start();        thread2.start();    }}

14、线程的等待和通知

  • 通过锁对象的调用,定义在Object类中
  • 等待wait:让线程进入阻塞状态wait():一直等待,直到被通知wait(毫秒):等待指定时长,线程可以被通知或自动唤醒
  • 通知notify:让等待状态的线程从阻塞状态恢复到就绪状态notify():通知一个等待线程notifyAll():通知所有等待线程
  • 注意,只有锁对象才能调用notify或者wait方法,必须在同步代码块的锁对象或同步方法的this调用。否则会出现IllegalMonitorStateException异常

15、wait和sleep的区别?

  • wait会释放锁资源,sleep不会
  • sleep是线程对象调用,wait是锁对象调用;
  • sleep必须等睡眠时间结束,wait可以等时间结束,也可以被唤醒

16、生产者消费者模式

  • 不是GOF23设计模式之一,是与线程相关的设计模式
  • 作用:解耦:让生产者消费者之间解耦,生产者消费者之间不会直接访问高并发:生产者消费者解耦,生产者和消费者不需要互相等待,生产者能处理更多的消费者请求解决忙闲不均:解决了生产者速度太快消费者需求太低,或者生产者速度太慢,消费者需求太大的问题
  • 实现过程:定义缓冲区,用于存放数据,缓冲区有上限生产者线程将生产数据存入缓冲区,当缓冲区满了,让生产者等待,如果没满,通知生产者继续生产消费者从缓冲区取数据,当缓冲区空了,让消费者等待,等有数据,通知消费者继续消费

17、阻塞队列

  • 是一种集合,根据数据的个数自动产生阻塞
  • BlockingQueue 父接口
  • 常用实现类:LinkedBlockingQueue 链表结构的阻塞队列 (插入删除效率高)ArrayBlockingQueue 数组结构的阻塞队列(查询效率高)
  • 常用方法put() 添加数据到末尾,如果达到上线,就阻塞当前线程take()从队列头删除数据,如果为空,就阻塞

18、线程池

18.1、线程的作用?

  • 回收线程资源,线程是一种很重要的资源,线程的创建和销毁都很消耗资源,频繁的创建和销毁线程会降低程序的性能
  • 注意:线程池中的线程执行完任务后不是直接死亡,而是回到池中,可以重复利用

18.2、线程池API

  • Executor接口execute(Runnable)执行单个线程任务
  • ExecutorService接口showdown:关闭shutdownNow:立刻关闭submit:提交
  • ThreadPoolExecutor 线程实现类
  • Executors 工具类帮助创建不同类型的线程池主要方法:ExecutorService newCachedThreadPool()长度不限的线程,不能控制并发量,速度更快//长度不限线程池 public static void testCachedThreadPool(){ ExecutorService threadPool = Executors.newCachedThreadPool();//长度不限的线程池 for (int i = 0; i < 10; i++) { threadPool.execute(()->{ System.out.println("当前执行的是"+Thread.currentThread().getName()); }); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } threadPool.shutdown(); } ExecutorService newFixedThreadPool(int) 长度固定的线程池,可以控制并发量,并发量大时,需要排队//长度固定线程池 public static void testFixedThreadPool(){ ExecutorService threadPool = Executors.newFixedThreadPool(5);//长度不限的线程池 for (int i = 0; i < 10; i++) { threadPool.execute(()->{ System.out.println("当前执行的是"+Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); } threadPool.shutdown(); } ExecutorService newSingleThreadExecutor() 单线程线程池//单线程线程池 public static void testSingleThreadPool(){ ExecutorService threadPool = Executors.newSingleThreadExecutor();//长度不限的线程池 for (int i = 0; i < 10; i++) { threadPool.execute(()->{ System.out.println("当前执行的是"+Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); } threadPool.shutdown(); } ScheduledExecutorService newScheduledThreadPool(int)可调度的线程池,执行线程时,可以设置执行周期和延时public static void testScheduleThreadPool(){ ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10); threadPool.scheduleAtFixedRate(()->{ System.out.println("当前执行的是"+Thread.currentThread().getName()); },5,1, TimeUnit.SECONDS); //threadPool.scheduleWithFixedDelay() }
  • 自定义线程池构造方法:ThreadPoolExector核心线程数:corePoolSize最大线程数:maximumPoolSize存活时间:keepAliveTime时间单位:timeUnit阻塞队列,保存执行任务(Runnable):workingQueue优化配置核心线程配置和最大线程数一样,减少创建新线程和销毁线程的开销核心线程数配置和cpu核心数相关, 核心数 * N (N >= 1) N具体看任务量、执行时间等情况阻塞队列使用LinkedBlockingQueue,添加和删除任务效率高
public static void myselfThreadPool(){        int processors = Runtime.getRuntime().availableProcessors();        System.out.println(processors);        int n = 2;        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(processors * n, processors * n, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue());        for (int i = 0; i < 10; i++) {            threadPool.execute(()->{                System.out.println("当前执行的是"+Thread.currentThread().getName());            });        }        threadPool.shutdown();    }

19、ThreadLocal

19.1、ThreadLocal是什么?

  • ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

19.2、syncronized 与 ThreadLocal的区别?

  • syncronized虽然能解决线程间变量的隔离的问题,解决的方法是当一个线程上锁之后,拒绝其他线程访问,当前只有一个线程在访问这个资源,但是这种方式需要对每一个线程都加锁,降低了程序的效率,也降低了程序的并发性
  • syncronized 以时间换空间,让所有线程排队访问,只提供了一份变量,侧重的是多个线程之间访问资源的同步
  • ThreadLocal不需要加锁,也能解决线程间变量的隔离性,可满足程序的高并发,变量只在线程的生命周期内起作用
  • ThreadLocal以空间换时间,为每一个线程都提供一份单独的副本,实现访问互不干扰,每个线程都访问自己独有的那份变量,使用ThreadLocal可以保证程序拥有更高的并发性。

19.3、ThreadLocal的优点?

  • 绑定参数:保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题
  • 线程隔离:各个线程之间的数据相互隔离又具有并发性,避免同步方法带来的性能损失

19.4、ThreadLocal使用场景

  • JDBC连接数据库,获取Connection的时候,为保证在高并发的情况下进行事务操作,保证在dao层getConnection()时候得到的对象和事务的Connection对象是同一个对象,就需要使用到ThreadLocal来实现线程之间的数据隔离,同时也不影响数据库连接的性能。
  • 解决方案,在DBUtils中定义ThreadLocal对象,当connection为空的时候,使用数据库连接池获取一个连接对象,然后将这个连接对象保存到ThreadLocal,之后调用getConnection()方法返回的是ThreadLocal中的connection,这样就实现了connection对象的隔离,需要注意的是,事务提交之后需要解绑当前线程绑定的连接对象,threadLocal.remove(),目的是为了避免内存泄漏

什么是线程单线程和多线程_什么是多线程?看我多线程七十二变,你能记住吗?...相关推荐

  1. 线程中如何使用对象_在 Flink 算子中使用多线程如何保证不丢数据?

    简介: 本人通过分析痛点.同步批量请求优化为异步请求.多线程 Client 模式.Flink 算子内多线程实现以及总结四部分帮助大家理解 Flink 中使用多线程的优化及在 Flink 算子中使用多线 ...

  2. openoffice转化太慢且不能多线程_专访橙光卿蓝蓝:多线程IP如何赢在起跑线?丨制鲜者IP作者...

    这是鲜喵的第 1353 篇吐血原创  喵族码字员:郭小蝈    编者按 纵观这几年的爆款剧集和电影,无不是IP改编而来.我们认为一部IP改编影视作品的成功,首先是文学IP作品的成功,是一个鲜活.打动人 ...

  3. 查看mysql的多线程_在for循环里使用多线程查询数据库

    其实如果你是因为每个查询任务都比较慢,所以想采用这种方式,不如去优化一下sql.或者你可以用下面的这种线程池的方式来处理,不过代码的复杂度会大大提高的. Futrue返回的包装的数据类型对应你sql返 ...

  4. 使用线程锁(lock)实现线程同步_一文搞懂Java多线程使用方式、实现原理以及常见面试题...

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  5. Python图片下载器(单线程PK多线程)_一蓑烟雨任平生

    文章目录 前言 一.单线程下载图片 二.多线程爬取图片 总结 前言 爬取图片很简单,但是单线程跟多线程的对比就不一样了,可以看到下载速度 一.单线程下载图片 # =================== ...

  6. python3 多线程_图解|为什么 Python 多线程无法利用多核

    (给Python开发者加星标,提升Python技能) 来源:后端技术指南针 1.全局解释锁 如题: Python的多线程为什么不能利用多核处理器? 全局解释器锁(Global Interpreter ...

  7. 什么是java多线程_什么是java多线程,java多线程的基本原理?

    1.什么是多线程? 多线程是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率.线程是在同一时间需要完成多项任务的时候被实现的. 2.线程的工作原理: 每当我们开启一个线程的时候,线程会为我们 ...

  8. java超线程_超线程多核心下Java多线程编程技术分析

    在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,本文主要讲述超线程多核心下Java多线程编程技术分析,更多Java专业知识,广州疯狂 ...

  9. JAVA入门_多线程_邮局派发信件

    JAVA入门_多线程_邮局派发信件 Postman package cn.campsg.java.experiment.entity;public class Postman {private Str ...

  10. Java线程之多线程与多进程(3)——Java中的多线程

    单线程 任何程序至少有一个线程,即使你没有主动地创建线程,程序从一开始执行就有一个默认的线程,被称为主线程,只有一个线程的程序称为单线程程序.如下面这一简单的代码,没有显示地创建一个线程,程序从mai ...

最新文章

  1. Java将mysql输出csv,如何从Java中的Access数据库导出表并将其保存到.csv
  2. C/S架构和B/S架构的概念和区别
  3. Linux快速查看某条命令的版本和存放的位置(ls -l `which mvn`)
  4. linux进程调度算法:分时调度策略、FIFO调度策略、RR调度策略
  5. boxfilter 函数
  6. 使用微软的 ilasm 和 ildasm 对. net程序进行编译和反编译
  7. PowerDesigner的汉化破解安装到逆向工程(ORACLE)
  8. python模块学习(1)
  9. [Gamma阶段]展示博客
  10. 做一个聪明的前端开发者
  11. C#学习常用类(1003)---Timer类(System.Timers.Timer)
  12. [原创]Clang with Microsoft CodeGen小测试
  13. dalvik虚拟内存管理之二——垃圾收集
  14. ElasticSearch+kibana安装
  15. 华为发布鸿蒙Beta版公测,华为推出鸿蒙OS 2.0手机开发者Beta版:P40、Mate 30系列可申请公测...
  16. 天正计算机命令大全,新手必看-史上最全CAD快捷键大全
  17. Ubuntu 20.04 源码编译Paddle2.2.2
  18. 联通物联卡为什么没有网络_物联网卡显示3g 联通物联网
  19. 计算机视觉知识点之RCNN/Fast RCNN/Faster RCNN
  20. strcmp函数的分析及实现

热门文章

  1. Hibernate的出现和Hinbernate的简单模拟实现
  2. 基于JAVA+SpringBoot+Mybatis+MYSQL的飞机订票系统
  3. ecplise git修改提交信息_Git提交信息规范化
  4. 剑指offer:按之字形打印二叉树(栈|双向队列+中序遍历)
  5. FastStone Capture 注册码 序列号
  6. windows常见快捷键
  7. html-webpack-plugin 中使用 title选项设置模版中的值无效
  8. 【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性
  9. Django学习资源
  10. 【HDU】1695 GCD