Java基础 (14) - 多线程

多线程的方式有Thread、Runnable、Callable、ExecutorService四种方式

1.Thread、Runnable区别

(1) Thread是类,Runnable是接口(支持多继承)。Thread可以理解成实现了Runnable接口的类。

(2) Runnable资源共享实现较简单。

(3) 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

(4) 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

 

2.Runnable、Callable区别

(1)Runnable是run() , Callable是call()。其中Runnable可以提交给Thread来包装下,Callable一般都是提交给ExecuteService和FutureTask来执行。

(2)Callable能获取返回值和抛出异常。

class c implements Callable<String>{

@Override

public String call() throws Exception {

return null;

}

}

class r implements Runnable{

@Override

public void run() {

}

}

设置当前线程的未捕获异常处理器

public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)

当未设置的时候会调用jvm默认的异常处理一下执行。

 

(3)Callable获取值时会导致所在的线程等待,直到get返回值为止,返回之前会中断当前线程。

future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态

future.isDone() //return true,false 无阻塞

future.get() // return 返回值,阻塞直到该线程运行结束

四种实现多线程方式:https://blog.csdn.net/u011480603/article/details/75332435/

 

3.启动线程

start()是真真的开启一个线程执行,会分配资源。

run()只是线程的一个普通方法,调用方法会进入调用者所在线程的等待执行队列(不会在新线程执行),当前面Runnable执行完毕后再执行。

 

4.停止线程

(1)使用退出标志(volatile 布尔变量),也就是当run方法完成后线程终止。

(2)使用interrupt / interrupted方法阻塞线程(修改线程中断标记值) ,isInterrupted()返回线程标记值。

调用interrupt()方法,立刻改变的是中断状态,但如果不是在阻塞态,就不会抛出异常;如果在进入阻塞态后(Object.wait, Thread.join和Thread.sleep),中断状态为已中断,就会立刻抛出异常

(3)使用stop方法强行终止线程  (这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)

附Join用法(同步):https://blog.csdn.net/qq_33236248/article/details/80266487

 

5.线程异常

线程出现异常结果

(1)线程运行 ->线程处理了异常或抛出

(2)线程停止 ->发生异常线程没有捕获或没有将其抛出,此时JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理

使用步骤

第一步:implements UncaughtExceptionHandler 自定义异常处理类

第二步:Thread t = new Thread(new ExceptionThread());

t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());

t.start();

 

6.线程同步

synchronized方法、synchronized模块(wait/notify机制)、ReenreantLock、volatile、while轮询等方式

(1)synchronized方法

由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 

内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态

 

(2)synchronized模块 - wait/notify机制

同步是一种高开销的操作,因此应该尽量减少同步的内容

通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可

 

(3)  ReenreantLock

JavaSE1.5.新增了一个java.util.concurrent包来支持同步。 
ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 
它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
ReenreantLock类的常用方法有:

        ReentrantLock() : 创建一个ReentrantLock实例 
        lock() : 获得锁 
        unlock() : 释放锁  - finally 中执行

从上可看出  他在写法上相对synchronized 更简洁

 

(4) volatile

可见性 - 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新.因此每次使用该域就要重新计算,而不是使用寄存器中的值

有序性 - 编译优化时 ,将不改变此修饰过的变量执行顺序

 

7.如何避免死锁?

Java多线程中的死锁 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:

1、互斥条件:一个资源每次只能被一个进程使用。

2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

3、不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

解决死锁

1、 加锁顺序

2、加锁时限

3、死锁检测

https://www.cnblogs.com/android-blogs/p/5765148.html

 

死锁代码、检测

https://www.cnblogs.com/itsoku123/p/10974157.html

 

8、线程状态

 

 

9.常见问题

(1)wait/notify机制

以生产者和消费者为例

调用任意方法,必须在对象锁方法体中

生产者调用notify后,生产者所在线程并不会立即退出线程,得等到生产者执行完毕。消费者才有可能机会执行

消费者被唤醒后是从wait()方法(被阻塞的地方)后面执行,而不是重新从同步块开头

 

(2)notifynotifyAll区别

notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。

notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行

 

(3)wait() sleep()有什么不同?

sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁

释放锁资源,进入资源等待池等待

参考:https://www.cnblogs.com/hapjin/p/5492619.html

 

(4)sleep yield区别

yield和 sleep 一样都是 Thread 类的方法,都是暂停当前正在执行的线程对象,和 sleep 不同的是 yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。还有一点和 sleep 不同的是 yield 方法只能使同优先级或更高优先级的线程有执行的机会

Thread相关方法

public static Thread.yield()  //当前线程可转让cpu控制权,让别的就绪状态线程运行(切换)

public static Thread.sleep()   //暂停一段时间

public join()//在一个线程中调用other.join(),将等待other执行完后才继续本线程。

public interrupte()//后两个函数皆可以被打断

参考:https://www.cnblogs.com/wxd0108/p/5479442.html

 

(4)Synchronized和ReentrantLock的区别

ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:

(1)ReentrantLock可以对获取锁的等待时间进行设置,就是给锁设定一个等待时间。如果过了等待时间线程还没有获取锁,那么线程就自动放弃获取该锁,这样就避免了死锁

(2)ReentrantLock可以获取各种锁的信息

(3)ReentrantLock可以灵活地实现多路通知

另外,二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word

 

参考:https://blog.csdn.net/qq_41212104/article/details/81773717

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值