天天看点

多线程知识点复习(第一次)

由于在我现阶段的学习过程中,接触的大都是单线程程序,所以对该板块的知识点总是差点味道。不过这些都是基础,很多东西最开始是不需要明白为什么的!!!

文章目录

    • 1、线程实现
      • 1.1、继承Thread类(重点)
      • 1.2、实现Runnable接口(重点)
      • 1.3、实现Callable接口(了解)
      • 1.4、多线程的底层
    • 2、Lambda表达式
      • 2.1、学会五种类的定义
      • 2.2、Lambda表达式的简化
    • 3、线程状态(重要)
      • 3.1、线程的五种状态
      • 3.2、线程相关的方法
    • 4、线程同步
      • 4.1、synchronized(Obj){}
      • 4.2、Lock
    • 5、线程通信问题
    • 6、思维导图

1、线程实现

1.1、继承Thread类(重点)

实现过程:

  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程

1.2、实现Runnable接口(重点)

实现过程:

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程

两种方式的比较:

继承Thread类

  • 子类继承Thread类具备多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性

实现Runnable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start(
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

1.3、实现Callable接口(了解)

实现过程:

  1. 实现Callable接口,需要返回值类型
  2. 重写call()方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

1.4、多线程的底层

多线程的底层实现是静态代理(复习设计模式的时候再复习该知识点)

2、Lambda表达式

任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口

2.1、学会五种类的定义

外部类:与主启动类同级下定义的一个新的类

静态内部类:在一个class的内部定义,并且使用了static修饰了类(与方法的成员变量同级)

局部内部类:在方法的内部定义,局部,可以理解为与局部变量同级的类

匿名内部类:不在出现class关键字,直接匿名,采用new接口的方式,直接书写方法体

Lambda表达式:类似于匿名内部类,直接将方法体,写成对应的Lambda形式

2.2、Lambda表达式的简化

1、常规情况

(int a) -> { System.out.println(“a是:” + a); };

2、可以省略参数类型

(a) -> { System.out.println(“a是:” + a); };

3、如果参数只有一个可以省略参数和括号

a -> { System.out.println(“a是:” + a); };

4、如果方法体只有一行,可以简写成这样

a -> System.out.println(“a是:” + a);

个人建议,良好的代码可读性也是衡量一个代码质量的标准,一味的彰显代码的精简便捷是不可取的!

3、线程状态(重要)

3.1、线程的五种状态

  • 创建状态:线程一旦创建就进入新生状态
  • 就绪状态:调用start()方法时,会进入该状态
  • 阻塞状态:使用sleep、wait或同步锁时,进入该状态。该状态恢复以后会进入就绪状态
  • 运行状态:线程进入运行状态的时候才是运行状态
  • 死亡状态:线程中断或者结束,无法再次启动

3.2、线程相关的方法

1、停止线程

不推荐使用JDK的方法用来停止线程。我们可以设置一个标志位,当线程运行到某个状态的时候,修改对应的状态位,然后停止线程

3、线程休眠

Thread.sleep形式

让线程进入阻塞状态,即睡眠指定的时间

4、礼让线程

Thread.yield()形式

让线程进行礼让。重新分配CPU的调度,即抢到资源的线程放弃机会,重新与其他线程一起竞争。所以礼让不一定成功。

5、强制执行线程

new Thread ().join()形式

当程序执行到了我们指定的状态时,强制让某一个线程执行某一种状态

6、观测线程状态

Thread.State state = thread.getState();

System.out.println(state);

使用该方法来接受线程的状态

线程的状态:

  • new:尚未启动的线程处于此状态
  • runnable:在java虚拟机中执行的线程处于此状态
  • blocked:被阻塞等待监视器锁定的线程处于此状态
  • waiting:正在等到另一个线程执行特定动作的线程处于此状态
  • timed_waiting:正在等到另一个线程执行动作达到指定等待时间的线程处于此状态
  • terminated:已推出线程处于此状态

7、线程的优先级

getPriority().setPriority(int xxx)

线程优先级的范围为1~10

可以在线程启动前设置优先级,优先级越高就越先执行(只是优先级高,并不一定真的就是先执行)

8、守护线程

thread.setDaemon(true);

将线程设置为守护线程以后,守护线程只会在用户进程结束以后再结束。比如我们的计算机操作日志,只要我们有一定的操作就会运行,当我们关闭电脑没有任何操作的时候守护线程也就结束。

4、线程同步

在我们的多线程环境下,会出现两个线程抢占同一个资源的情况,继而会导致信息的冲突。为了避免多线程环境下的这种资源冲突,多线程中引入了线程同步这一个概念。是想方式是使用锁机制。

即当第一个线程拿到资源的时候,就带着一把锁,用来向其他的线程表示,该资源已经有人了。当线程处理完该线程以后就释放这把锁,然后其他的线程就能够获取对应的资源了。

这里面有点需要记忆,第一就是使用这样的锁机制能够保证线程安全,第二就是使用了锁机制会带来效率上的降低。

4.1、synchronized(Obj){}

使用synchronized关键字,可以对指定的方法或者指定的代码块进行锁定。

其实在很多代码中都能看到synchronized关键字,比如之前的Vector集合。

直接添加在对应的方法上即可

加了synchronized关键字以后代码的执行过程:

  1. 第一个线程访问时,锁定同步监视器,执行对应的代码
  2. 第二个线程访问时,发现同步监视器被锁定了,无法访问
  3. 第一个线程访问完毕,解锁同步监视器
  4. 第二个线程访问时,发现同步监视器锁消消失,然后自己对同步监视器进行锁定,然后访问

补充

死锁产生的必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:与个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件:若干进程之间形成一种头尾想接的循环等待资源关系

只要破坏以上任意一个就可以导致死锁

设想一下,你需要对方手里的资源,对方需要你手里的资源,那不就发生冲突了?

4.2、Lock

private final ReentrantLock lock = new ReentrantLock();

lock.lock();

lock.unlock();

Lock是显式锁,怎么理解显式锁呢?

我理解的显式定义,再显式的释放

Lock锁的引入使得程序锁机制更加的灵活,其lock锁的功能比synchronized更加强大

使用显式锁的时候,建议是同try…catch…finally语句,将释放锁语句放在finally语句块中,避免没有释放锁引发一系列的冲突。

Lock锁是JDK的一个接口功能,synchronized是Java关键字,是基于JVM层面实现的。简单来说就是,synchronized是java语言最开始设计时候为了解决并发特性所以使用的锁机制,lock锁是后期开发人员想使用更加丰富的锁功功能引入的。

5、线程通信问题

线程与线程之间的通信可以使用两种方法

管程法:

生产者将生产好的数据放入指定的数据缓冲区,然后消费者去缓冲区里面拿数据就行了。生产者生产了产品,放入指定的缓冲区中,消费者对缓冲区进行判定,有就拿取数据,没有就通知生产者生产

信号灯法:

设置一个标志位,用来充当信号灯。生产者生产了商品,点亮信号灯,消费者看到亮着的灯就进行消费。

线程池概念:

  • 由于我们经常的创建和销毁特别大的资源,对于性能损耗十分的巨大。所以引入了线程池,可以类比于java的常量池,数据库的数据库连接池
  • 当我们创建了多个线程,就将线程放入线程池。当我们需要使用对应线程的时候,就去对应的线程池中获取,减小了系统在频繁创建销毁活成中的性能损耗

6、思维导图

多线程知识点复习(第一次)