前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java里面的多线程 (最全 最精美 ) 不好你打我

Java里面的多线程 (最全 最精美 ) 不好你打我

作者头像
CaesarChang张旭
发布2021-01-26 14:17:10
4400
发布2021-01-26 14:17:10
举报
文章被收录于专栏:悟道

~关注我 带你看更多精品技术和面试必备

多线程技术概述

进程 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间 线程 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少 有一个线程 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分 成若干个线程

线程调度 分时调度 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 抢占式调度 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度

CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻, 只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时 刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使 用率更高。(CPU执行一次输入函数, 切换到人输入,然后人输入 人输入..... 会比 CPU之心输入 人输入,然后后在调用输入函数 ,人再输入 快一些 ,)

同步与异步 同步: 排队执行 , 效率低但是安全. 异步: 同时执行 , 效率高但是数据不安全. 并发与并行 并发:指两个或多个事件在同一个时间段内发生。 并行:指两个或多个事件在同一时刻发生(同时发生)

Thread类

Thread类构造方法: 1.Thread(); 2.Thread(String name); 3.Thread(Runable r); 4.Thread(Runable r, String name); //name为 线程名称 常用方法 static Thread currentThread() 返回对当前正在执行的线程对象的引用。 long getId() 返回该线程的标识符。 String getName() 返回该线程的名称。 int getPriority() 返回线程的优先级。 void interrupt() 中断线程。 boolean isAlive() 测试线程是否处于活动状态。 void join() 等待该线程终止。 void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。 void join(long millis, int nanos) 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。 void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 void setPriority(int newPriority) 更改线程的优先级。 static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 static void yield() 暂停当前正在执行的线程对象,并执行其他线程。

多线程的实现方式:

1、继承Thread类实现多线程

Thread本质上也是实现了Runnable接口的一个实例 启动线程的唯一方法就是通过Thread类的start()实例方法。

public class MyThread extends Thread {   public void run() { //自定义进程中的内容    System.out.println("MyThread.run()");   } }

2 实现Runnable接口方式实现多线程(更常用)

自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口

public class MyThread extends OtherClass implements Runnable {   public void run() { //自定义进程中的内容    System.out.println("MyThread.run()");   } } //在主函数里面还得借用Thread MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start();

"好处:" 1 创建任务,然后给线程分配方式来实现的多线程,更适合多个线程同时执行相同任务. 2 可以避免单继承带来的局限性 3 任务线程分离, 提高了程序的健壮性 4 后续的线程池技术,只接受Runnable类型的任务 ,不接受Thread类型的线程

3、实现Callable实现有返回结果的多线程

1 同样创建一个类实现Callable接口; 2:通过futureTask类使用其传递Callable接口作为参数的有参构造方法; 3:使用thread的有参构造; 4:t1.start()启动线程 5:启动线程后,通过futureTask.get()方法获取到线程的返回值。 futrueTask.get()方法放到最后,这样就不会影响主线程了。如果get方法放在前面的话,会造成主线程阻塞,等到futrueTask运行完成之后,才继续执行自己的逻辑。这样就失去了开启线程的意义了!!!

关闭线程方式:

1 标志位

2 使用 interrupt终止线程

我们直接创建的线程为 用户线程 设置为守护线程, t.setDaemon(true); t.start() ; //一定要在开始之前设置为守护线程

线程安全问题:

1 同步代码块

注意 :不能每个线程看一个锁,应该全部线程 看同一把锁 那么 Object o=new Object() 只可以呗new 一次

2 同步方法:

在方法前面加上 synchronized

3 显示锁

用的时候 l.lock(); 结束了 l.unlock();

显示锁和隐示锁区别 隐式锁:synchronized;显式锁:lock 所谓的显示和隐式就是在使用的时候,使用者要不要手动写代码去获取锁和释放锁的操作。 1 出身不同: sync是底层是通过monitorenter进行加锁 只有在同步块或者是同步方法中,JVM才会调用monitory对象的 通过monitorexit来退出锁的。 lock是通过调用对应的API方法来获取锁和释放锁的。 2 使用方式不同 sync代码块执行完成之后,系统会自动的让程序释放占用的锁。Sync是由系统维护的,如果非逻辑问题的话话,是不会出现死锁的。 在使用lock的时候,我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。手动获取锁方法:lock.lock()。释放锁:unlock方法。需要配合tyr/finaly语句块来完成。 3等待是否可中断 Sync是不可中断的。除非抛出异常或者正常运行完成 Lock可以中断的。中断方式: 1:调用设置超时方法tryLock(long timeout ,timeUnit unit) 2:调用 lockInterruptibly() 放到代码块中,然后调用 interrupt() 方法可以中断 4加锁的时候是否可以公平 Sync;非公平锁 lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。 5锁绑定多个条件来condition Sync:没有。要么随机唤醒一个线程;要么是唤醒所有等待的线程。 Lock:用来实现分组唤醒需要唤醒的线程,可以精确的唤醒,而不是像sync那样,不能精确唤醒线程。 6从使用锁的方式比较

公平锁 非公平锁: Sync;非公平锁 lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。 公平锁和非公平锁都会维护一个等待队列,区别在于非公平锁进来时可能刚好获得锁,不用去等待队列,公平锁会检查当前等待队列是否有等待线程且是队列头,才去获得锁。 公平锁优缺点:

  • 优点:所有的线程都能得到资源,不会饿死在队列中。
  • 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

非公平锁优缺点:

  • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
  • 缺点:你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

区别在于 非公平锁减少线程上下文切换。 所以非公平锁比公平锁效率高。

线程死锁:

线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。

死锁产生的条件

  • 互斥条件:一个资源,或者说一个锁只能被一个线程所占用,当一个线程首先获取到这个锁之后,在该线程释放这个锁之前,其它线程均是无法获取到这个锁的。
  • 占有且等待:一个线程已经获取到一个锁,再获取另一个锁的过程中,即使获取不到也不会释放已经获得的锁。
  • 不可剥夺条件:任何一个线程都无法强制获取别的线程已经占有的锁
  • 循环等待条件:线程A拿着线程B的锁,线程B拿着线程A的锁。。

线程的六种状态

NEW 初始状态,线程刚被构建,但是还没有调用start()方法Runable 运行状态,Java系统系统中将操作系统中的就绪和运行两种状态笼统地称为“运行中”Blocked 阻塞状态,表示线程阻塞于锁waitting 等待状态,表示线程进入等待状态,进入该状态表示当前线程做出一些特定动作(通知或者中断) TIME_WAITTING 超时等待状态,该状态不同于等待状态,它可以在指定的时间后自行返回Terminated 中止状态,表示当前线程已经执行完毕

线程池:

线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

4种线程池 1缓存线程池 判断线程池是否存在空闲线程 * 存在则使用 * 不存在,则创建线程 并放入线程池, 然后使用 ExecutorService service = Executors.newCachedThreadPool(); //向线程池中 加入 新的任务 service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); 2. 定长线程池 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 ExecutorService service = Executors.newFixedThreadPool(2); 2为传入的初始线程数 service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); 3. 单线程线程池 只有一个线程, 判断线程池 的那个线程 是否空闲 * 2. 空闲则使用 * 4. 不空闲,则等待 池中的单个线程空闲后使用 ExecutorService service = Executors.newSingleThreadExecutor(); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); service.execute(new Runnable() { @Override public void run() { System.out.println("线程的名称:"+Thread.currentThread().getName()); } }); 4. 周期性任务定长线程池 判断线程池是否存在空闲线程 * 2. 存在则使用 * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 周期性任务执行时: * 定时执行, 当某个时机触发时, 自动执行某任务 . ScheduledExecutorService service = Executors.newScheduledThreadPool(2); /** * 定时执行 * 参数1. runnable类型的任务 * 参数2. 时长数字 * 参数3. 时长数字的单位 */ /*service.schedule(new Runnable() { @Override public void run() { System.out.println("俩人相视一笑~ 嘿嘿嘿"); } },5,TimeUnit.SECONDS); */ /** * 周期执行 * 参数1. runnable类型的任务 * 参数2. 时长数字(延迟执行的时长) * 参数3. 周期时长(每次执行的间隔时间) * 参数4. 时长数字的单位 */ service.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("俩人相视一笑~ 嘿嘿嘿"); } },5,2,TimeUnit.SECONDS); }

Lambda表达式

lambda表达式允许你通过表达式来代替功能接口。 基本语法: (parameters) -> XXX(parameters) ->{ XXX; } 1、参数列表:与接口中方法的参数列表要求一致 2、箭头:-> 由横线和大于号构成 3、方法体:和方法的方法体要求是一样的

小常识:,匿名内部类要么继承一个父类,要么实现一个接口

“接口是不可以被new的,但是有一种写法是 new Person(){},似乎是接口被new了,其实不然,注意后面的{},这其实是匿名内部类。”

看完请进入 Java网络基础 别忘了给我点个赞 在我脑子里跑了这么久了毕竟嘿嘿 么么哒

点击---->Java网络基础点我进入

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/09/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ~关注我 带你看更多精品技术和面试必备
  • 多线程技术概述
    • Thread类
      • 多线程的实现方式:
        • 1、继承Thread类实现多线程
          • 2 实现Runnable接口方式实现多线程(更常用)
            • 3、实现Callable实现有返回结果的多线程
              • 关闭线程方式:
                • 线程安全问题:
                  • 线程死锁:
                  • 线程的六种状态
                  • 线程池:
                  • Lambda表达式
                  • 看完请进入 Java网络基础 别忘了给我点个赞 在我脑子里跑了这么久了毕竟嘿嘿 么么哒
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档