Java并发编程_多线程

 并发基础

为什么要使用并发编程

并发编程可以提高性能

什么是上下文切换

多线程编程中,当前任务在执行完CPU时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以在加载这个任务的状态。

(任务从保存到在加载的过程就是一次上下文切换)

并发编程有什么缺点

线程安全,死锁等问题

串行,并行,并发的区别

串行:多个任务 由一个线程按顺序执行

并行:多个任务 由多个处理器同时执行

并发:多个任务 在同一个CPU核交替执行

Java线程

进程和线程的区别是什么?

进程是处于运行状态的应用程序,

而线程是进程里面的一个执行序列

(一个进程可以有多个线程)

线程有哪些状态? 生命周期?

新建, 运行, 可运行, 阻塞, 死亡

(不同线程要抢CPU的使用权, 谁抢到了, 谁就处于运行状态)

怎么启动线程?

调用start()方法启动线程

如何停止一个线程

run方法执行完后,线程会自动终止

调用interrupt()方法中断线程

调用stop() 强行终止线程

多线程的实现方式 / 创建线程有几种不同的方式?

3种方式

写一个类

继承Thread类,重写run()方法

实现Runnable接口,重写run()方法

实现Callable接口,重写call()方法

然后调用start()方法启动线程

runnable和callable有什么区别

相同点:

都是接口;

都可以编写多线程程序

都采用 Thread.start()启动线程

区别:

Runnable接口的run方法没有返回值,无法抛出返回结果的异常

Callable接口的call方法有返回值,Callable接口支持返回执行结果,需要调用FutureTask.get()得到

什么是守护线程

守护线程在后台运行,周期性的执行某种任务,最典型的就是java中的垃圾回收线程

线程的run()和start()有什么区别

start()方法用于启动线程,run()方法用于执行线程的任务

run()方法只是线程的一个函数,调用run()方法不能说明是多线程,调用了start()方法才表示实现了多线程运行

线程类的构造方法,静态块是被哪个线程调用的

线程的run方法都是线程自己调用的

线程类的构造方法、静态块,是new实例所在的线程调用的

与线程相关的方法

wait():使线程处于等待/阻塞状态,并且释放所持有的对象锁

sleep():使线程处于睡眠状态,不会 释放对象锁,时间到了继续执行下面的代码

notify():唤醒一个处于等待状态的线程,由JVM确定唤醒哪个线程

notifyAll():唤醒所有处于等待状态的线程,只有获得锁的线程才能进入可运行状态

(线程被唤醒后,并不能马上执行,而是要获取该线程相应的对象锁才能运行)

sleep()和wailt()有什么区别

sleep()来自Thread,wait()来自Object

sleep()不释放锁,时间到会自动恢复

wait()会释放锁,可以使用notify()/notifyAll()唤醒

sleep()通常用于暂停执行

wait()通常用于线程间交互/通信

(sleep是占着CPU 却不用, wait是等待CPU的使用权)

notify()和notifyAll()有什么区别

notifyAll()会唤醒所有的线程,notify()只会唤醒一个线程

notifyAll()调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。

而notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制

为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类?

Java中,任何对象都可以作为锁,而任何对象都可以调用的方法一般定义在Object类中

怎么调用wait()方法?使用if块还是循环?为什么

wait()方法应该在循环中调用,因为当线程获取到锁时,其他条件可能还没满足,

所以使用循环检测条件是否满足会更好

Java线程数量过多会造成什么异常

消耗过多的CPU,给垃圾回收器带来压力

降低JVM的稳定性,可能抛出OOM异常 (OutOfMemoryError)

Java线程池

什么是线程池

线程池是用于管理线程的一个工具

就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销

线程池有什么优点

降低资源消耗

提高响应速度

提高线程的可管理性

(使用线程池可以进行统一的分配,调优和监控)



怎么创建一个线程池

可以使用Executors工具类创建线程池

也可以使用ThreadPoolExcutor创建自定义线程池 (阿里规范)

Executor和Executors的区别

Executor接口对象能执行我们的线程任务,

Executors工具类可以创建线程池

Executors和ThreadPoolExecutor创建线程池的区别

Executors的方法可能会耗费非常大的内存,甚至是OOM

ThreadPoolExecutor创建线程池的方式只有一种,就是走它的构造函数,参数自己指定

ThreadPoolExecutor构造函数的参数有哪些

线程池有哪些状态

线程池中submit()和execute()方法有什么区别

execute():只能之行Runnable类型的任务

submit():可以执行Runnable和Callable类型的任务

Callable类型的任务可以获取执行的返回值,而Runnable执行无返回值

如果你提交任务时,线程池队列已满,这时会发生什么

如果使用的是的是无界队列LinkedBlockingQueue,没关系,继续添加任务到阻塞队列中等待执行即可

如果使用的是有界队列 比如ArrayBlockingQueue,任务首先会被添加到有界队列中,有界队列满了,会根据maxmumPoolSlize的值增加线程数量,如果增加了线程数量还是处理不过来,就会拒绝新任务的处理

线程安全

什么是线程安全

当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

实现线程安全有几种方式?

3种

方法一:使用安全类,比如Java.util.concurrent下的类

方法二:使用自动锁synchronized (同步方法,同步代码块)

方法三:使用手动锁Lock

说明:加锁会让线程安全, 但其他请求都要等拿到锁的人释放锁才能访问, 会比较慢, 用户体验不太好



synchronized的作用?

synchronized关键字是用来控制线程同步的,就是在多线程环境下,控制synchronized代码段不被多个线程同时执行

一旦方法或者代码块被synchronized修饰,那么这部分就放入了监视器Monitor的监视范围,一次只能有一个线程执行该部分的代码,线程在获取锁之前不允许执行该部分的代码

synchronized和volatile的区别

volatile只能修饰变量

synchronized可以修饰类,方法,代码段

volatile可以实现共享变量的可见性和禁止指令重排序

synchronized可以实现变量的可见性和原子性

synchronized是悲观锁,属于抢占式,会引起其他线程阻塞

volatile修饰的变量不会在多个线程中存在复本,直接从内存读取

synchronized和Lock的区别

synchronized是Java的一个关键字,Lock是一个接口;

synchronized会自动释放锁,而Lock要手工释放,并且必须在finally代码块中释放

同步方法和同步代码块的区别是什么?

他们的锁对象 不一样

同步块和同步方法,那个是更好的选择

同步的范围越小越好,同步范围越大 其他线程等待的时间越长,越容易发生死锁

同步块只在需要锁住的代码块锁住相应的对象

同步方法会锁住整个方法

所以说同步块是更好的选择

线程B怎么知道线程A修改了变量

用volatile修饰变量

或者用synchronized修饰修改变量的方法

当一个线程进入一个对象的一个synchronized方法后,其他线程是否可进入此对象的其他方法

如果其他方法没有加synchronized关键字 就可以

并发锁

悲观锁和乐观锁的区别

悲观锁:

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁  

多线程的synchronized

数据库中的行锁,表锁等都是悲观锁,

都是在操作之前先上锁,

乐观锁:

每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制

乐观锁的实现方式

使用版本标识来确定读到的数据与提交时的数据是否一致。提交后 修改版本标识,不一致时可以采取丢弃和再次尝试的策略

公平锁与非公平锁:

公平锁,先对锁发出获取请求的一定先获得锁。非公平锁则反之(性能更高)。

什么是死锁

死锁会让程序一直卡住,无法向下执行

我们只能通过中止并重启的方式来让程序重新执行

造成死锁的原因

当前线程拥有其他线程需要的资源

当前线程等待其他线程已拥有的资源

都不放弃自己拥有的资源

从而陷入无限等待

避免死锁的方法

1.所有线程以固定的顺序来获得锁

2.缩小加锁的范围,仅操作共享变量时才加锁,尽量不要几个功能用同一把锁

3.使用tryLock()定时锁,设置超时时间,超时可以退出防止死锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值