线程学习总结

1.线程和进程

1.1进程的概念

    进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算。

1.2线程的概念

   线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.

 1.3多线程并发和并行

       并发:两个或多个事件在同一时间间隔发生。

       并行:两个或者多个事件在同一时刻发生。

 并行是真正意义上,同一时刻做多件事情,而并发在同一时刻只会做一件事件,只是可以将时间切碎,交替做多件事情。

网上有个例子挺形象的:

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。

你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

2.线程的创建

2.1继承Thread类

如果直接调用run方法,那么会先执行run方法线程里面的代码,在执行主线程里面的代码

如果调用start方法,那么run方法线程就会和主线程交替执行

2.2实现Runnable接口

2.3实现callable接口

知道可以用这个接口创建线程,但是没用过

3.线程状态

3.1线程状态图

 3.2线程方法

 3.2.1 setPriority方法(更改线程优先级)

 结果

通过结果我们也能看出,优先级高的线程并不一定先执行,只是大概率事件。

记住setPriority()方法更改线程优先级,getPriority()方法显示优先级

3.2.2 sleep方法(线程休眠)

 sleep方法的作用就是让当前线程休眠一段时间,这个时间我们是可以设置的,后面还有一个wait()方法也是线程休眠,文章后续会讲,我们要记住sleep方法不会释放锁

3.2.3 join方法(通俗一点讲就是线程插队)

 可以看到在主线程执行到200次的时候,我们强制让我们创建的线程执行

3.2.4 yield方法(线程礼让)

yield方法就是在执行当前线程时,当前线程不执行了,让下个线程执行,等下个线程执行完了,在执行当前的线程,但是线程礼让不一定成功,看CPU心情。

结果

 根据我们写的代码,如果不加yield方法的话,那么应该是线程a执行和结束完后,才是线程b的执行和结束,而不是线程a还没有结束,线程b就开始执行。

 3.2.5线程终止(我们一般不使用系统方法,而是自己写一个终止方法)

 我们先设置一个标志位,通过标志的true和false来判断标志位是否执行,可以看见当i=800,我们调用了stop01方法,然后flag变成false,然后while循环终止,这样就完成了线程终止

3.2.6 观测线程状态

 通过getState方法去获取线程状态

3.2.7 线程守护

 守护线程是用户线程死亡后,他也跟着死亡,如同Gom里面的上帝线程应该一直执行,但是you类里面的线程死亡后,他也跟着死亡。线程守护方法是getDaemon方法

4.线程同步

多个线程操作同一个资源时,就会发生线程并发,解决线程并发的方法就是加锁

4.1 synchronized

 在这里是三个线程去操作同一个资源(票),这时候就会发生线程并发问题

 

 可以看见第17张票,3个线程都说自己拿到了,但是这是不可能的,所以我们加上synchronized锁,解决线程同步

4.1.1 synchronized锁的加锁场景范围

 4.2 Lock锁

 要注意的是使用Lock锁必须要解锁,最好在finally代码块里面解锁

4.3 死锁

我理解的死锁就是多个线程都含有对方想要的资源,然后都不想把自己的给别人,形成了僵持

可以看见一个线程获得了资源a,然后又想获得资源b,但是另一个线程已经获得了资源b,平且基本上跟获得资源a的线程是同时获得的,这样就形成了死锁

 解决方法也很简单,就是让一个代码块不要同时拥有两个以上对象的锁

 

 4.3.1死锁形成的条件

1.互斥条件:一个资源只能被一个线程使用

2.请求与保持条件:一个线程因为请求资源而阻塞时,对已获得资源保持不放

3.不剥夺条件:进程已获得的资源,在未使用完前,不得剥夺

4.循环等待条件:若干线程之间形成了头尾相连的循环等待资源关系

打破死锁的方法也很简单,就是破坏其中一个或多个条件即可

4.4 Lock锁与synchroized锁的区别

1)Lock 是一个接口;synchronized 是 Java 中的关键字

2)Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,很可能会造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;synchronized 不需要手动获取锁和释放锁,在发生异常时,会自动释放锁,因此不会导致死锁现象发生;

3)Lock 的使用更加灵活,可以有响应中断、有超时时间等;而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,直到获取到锁;

4)在性能上,随着近些年 synchronized 的不断优化,Lock 和 synchronized 在性能上已经没有很明显的差距了,所以性能不应该成为我们选择两者的主要原因。官方推荐尽量使用 synchronized,除非 synchronized 无法满足需求时,则可以使用 Lock。

5. 线程通信

5.1 生产者与消费者模式

 5.2 线程通信

 5.3 信号灯法

解决生产者与消费者问题

 

 

 首先设置一个标志位,标志位为true时,开始执行生产者,为false时执行消费者。首先没有节目。所以休眠。然后开始录制节目,在使用notify唤醒线程,这样类推下去。

5.4 waie与sleep的区别

来源不同:sleep() 来自 Thread 类,wait() 来自 Object 类。

对于同步锁的影响不同:sleep() 不会该表同步锁的行为,如果当前线程持有同步锁,那么 sleep 是不会让线程释放同步锁的。wait() 会释放同步锁,让其他线程进入 synchronized 代码块执行。

使用范围不同:sleep() 可以在任何地方使用。wait() 只能在同步控制方法或者同步控制块里面使用,否则会抛 IllegalMonitorStateException。

恢复方式不同:两者会暂停当前线程,但是在恢复上不太一样。sleep() 在时间到了之后会重新恢复;wait() 则需要其他线程调用同一对象的 notify()/nofityAll() 才能重新恢复。

6. 线程池

 6.1.为什么要使用线程池

  经常创建和销毁,使用量大的资源时,比如并发下的线程,对性能影响很大

6.2.使用线程池的好处

  1.提高响应速度

  2.降低资源消耗

  3.便于线程管理

6.3.线程池中的几个关键方法

  1.corePoolSize->线程池的大小

  2.maximumPoolSize->最大线程数

  3.keepAliveTime->线程没有任务时最多保持多少时间后终止

代码演示

结果

Guess you like

Origin blog.csdn.net/qq_47499256/article/details/121459659