多线程-初阶(synchronized关键字和volatile关键字waitsleep 死锁)
Posted 秃头小宝儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程-初阶(synchronized关键字和volatile关键字waitsleep 死锁)相关的知识,希望对你有一定的参考价值。
多线程编程
1.Synchronized关键字
(1)synchronized的实现
- 1.针对操作系统层面,它是依靠互斥锁mutex.
- 2.针对JVM,使用监视器锁(monitor)来实现。
锁信息 monitor:
- 3.针对Java语言来说,是将锁信息存放在对象头(标识,标识锁状态/锁的拥有者)
(2)synchronized的3种使用场景
1.使用synchronized修饰代码块(可以给任意对象进行加锁)
2.使用synchronized来修饰静态方法(对当前的类进行加锁)
3.使用synchronized来修饰普通方法(对当前类实例进行加锁)
(3)synchronized锁升级的过程(jdk 1.7优化)
重量级:用户态–>内核态(有特别大的性能开销)
(4)synchronized和Lock的区别
- 1.关键字不同
- 2.synchronized自动进行加锁和释放锁,而Lock需要手动的加锁和释放锁。
- 3.Lock是 Java层面的锁的实现,而synchronized是Jvm层面的实现。
- 4.synchronized和 Lock适用范围不同,Lock只能用来修饰代码块,而synchronized即可以修饰代码块,还可以修饰静态方法和普通方法。
- 5.synchronized锁的模式只有是非公平锁模式,而Lock既可以使用公平锁,也可以使用非公平锁的模式。
- 6.Lock的灵活性更高(tryLock)。
(5)Lock手动锁
// 1.创建lock实例
Lock lock = new ReentrantLock(true);//公平锁
Thread t1 = new Thread(new Runnable()
@Override
public void run()
for (int i = 0; i < maxSize; i++)
// 2.加锁
lock.lock();
try
// 业务操作
number++;
finally
// 3.释放锁
lock.unlock();
);
注意事项:lock()操作一定要放在try外面。如果放在try里面可能造成两个问题:
- 1.如果try里面抛出异常了,还没有加锁成功就执行finally里面的释放锁的操作。因为还没有加锁就释放锁。
- 2.如果放在try里面,如果没有锁的情况下试图释放锁,这个时候产生的异常就会将业务代码(try里面的异常)给吞噬掉,增加了代码调试的难度。
- 如果一定想把lock放在 try 里面的话,一定记得放在第一行。
(6)公平锁和非公平锁
公平锁调度:
1.一个线程释放锁。
2.(主动)唤醒”需要得到锁“的队列来得到锁。
非公平锁:当一个线程释放锁之后,另一个线程刚好执行到获取锁的代码就可以直接获取锁。
在Java语言中所有的锁的默认实现方式都是非公平锁:
synchronized是非公平锁
ReentrantLock 默认是非公平锁,但也可以显示的声明为公平锁。
显示的声明公平锁:
Lock lock=new ReentrantLock(true);
2.volatile关键字
volatile总结:volatile可以解决内存不可见和指令重排序的问题,但是不能解决原子性问题。
3.通信-对象的等待集wait set
(1)wait()方法
方法wait() 的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,调用wait方法前,必须先加锁synchronized(object)。
解决 wait /notify随即唤醒的问题—LockSupport park() /unpark(线程)
LockSupport虽然不会报Interrupt的异常,但依旧可以监听到线程中止的指令。
public static void main(String[] args) throws InterruptedException
Object object = new Object();
synchronized (object)
System.out.println("等待中...");
object.wait();//一定要先加锁后休眠
System.out.println("等待已过...");
System.out.println("main方法结束...");
注意事项:
当使用吧wait()方法时,当不传递任何参数时,线程进入waiting状态,当传递的参数大于 0 时,线程进入timed_waiting状态。
当调用 wait()不传递参数的时候,其底层实现也是调用了wait(0)这个方法。
(2)面试题:wait和sleep区别?
相同点:
- 1.wait 和 sleep都是让线程进行休眠状态。
- 2.wait 和 sleep在执行的过程中都可以接收到终止线程执行的通知。
不同点:
- 1.wait 使用必须配合 synchronized一起使用,而sleep不用。
- 2.wait 在执行的时候会释放锁,而使用sleep不会释放锁。
- 3.wait是Object的方法,sleep是Thread(线程)的方法。
- 4.默认情况下wait(不传递任何参数或者是参数为0的情况下)它进入waiting状态,而sleep会进入timed_waiting状态。
- 5.使用wait时可以主动的唤醒线程,而使用sleep时不能主动的唤醒线程。
(3)为什么wait会释放锁?而sleep不会释放锁?
sleep必须要传递一个最大等待时间的,也说sleep是可控的(对于时间层面来说),而wait 是可以不传递参数的,从设计层面来讲如果让wait这个没有超时等待时间的机制不释放锁的话,那么线程可能会一直阻塞,而sleep不存在这个问题。
(4)为什么wait是Object的方法?而sleep是Thread的方法?
wait需要操作锁,而锁是属于对象级别(所有的锁都是放在对象头中的),它不是线程级别,一个线程中可以有多把锁,为了灵活起见,所以wait放在Object当中。
(5)sleep(0)和 wait(0)有什么区别?
1.sleep(0) 表示 0毫秒之后继续执行,而wait(0)表示一直休眠。
2.sleep(0)表示重新触发一次CPU 竞争。
(6)wait和 notify的使用:
public static void main(String[] args) throws InterruptedException
Object lock = new Object();
Thread t1 = new Thread(new Runnable()
@Override
public void run()
synchronized (lock)
try
System.out.println("t1 wait 之前");
lock.wait();
System.out.println("t2 wait 之后");
catch (InterruptedException e)
e.printStackTrace();
, "t1");
t1.start();
Thread t2 = new Thread(new Runnable()
@Override
public void run()
synchronized (lock)
try
System.out.println("t2 wait 之前");
lock.wait();
System.out.println("t2 wait 之后");
catch (InterruptedException e)
e.printStackTrace();
, "t2");
t2.start();
Thread t3 = new Thread(new Runnable()
@Override
public void run()
synchronized (lock)
try
System.out.println("t3 wait 之前");
lock.wait();
System.out.println("t3 wait 之后");
catch (InterruptedException e)
e.printStackTrace();
, "t3");
t3.start();
Thread.sleep(500);
System.out.println("主线程调用唤醒方法");
// 在主线程中唤醒线程 t1
synchronized (lock)
lock.notify();
// lock.notifyAll();
wait注意事项:
1.wait方法在执行之前必须先加锁,也就是说wait方法一定要配合synchronized一起使用。
2.wait和notify在配合synchronized使用时,一定要注意使用同一把锁。
3.wait 和 notify在配合使用时,一定要操作同一把锁。
4.死锁
定义:在两个或者两个以上线程运行中,因为资源抢占而造成线程一直等待的问题。
(1)造成死锁的4个条件
- 1.互斥条件:当资源被一个线程拥有之后,就不能被其他的线程拥有了。(不可更改)
- 2.请求拥有条件:当一个线程拥有了一个资源后又试图请求另一个资源。(可以解决)
- 3.不可剥夺条件:当一个资源被一个线程拥有之后,如果不是这个线程主动释放此资源的情况下,其他线程不能拥有此资源。(不可更改)
- 4.环路等待条件:两个或者两个以上的线程在拥有了资源之后,试图获取对方资源的时候形成了一个环路。(可以解决)
(2)如何解决死锁问题?
解决造成死锁的4个条件中的 请求拥有条件或者 环路等待条件。
控制加锁的顺序。
(3)精简版的死锁代码
synchronized(lockA)
Thread.sleep(1000);
synchronized(lockB)
//业务代码。。。
synchronized(lockB)
Thread.sleep(1000);
synchronized(lockA)
//业务代码。。。
以上是关于多线程-初阶(synchronized关键字和volatile关键字waitsleep 死锁)的主要内容,如果未能解决你的问题,请参考以下文章