JAVAEE---多线程3

synchronized

1.解释synchronized的意思

synchronized这个词是同步的意思,它在计算机中存在多种意思,不同的上下文中会有不同的含义

比如:

2.synchronized 的特性

(1)互斥                (2)刷新内存              (3)可重入

下面我们来一个个解释一下:

(1)互斥
synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待.

 (2)刷新内存

(3)可重入
synchronized是可重入锁

解释下可重入锁和不可重入锁:

可重入锁:同一个线程针对同一个锁可以进行连续加锁,不会出现死锁的情况
不可重入锁:同一个线程针对同一个锁不可以进行连续加锁,会出现死锁的情况

可重入锁内部是怎么运作的

可重入锁的意义就是----> 降低了程序员的负担(提高了开发效率)
但是使用 可重入锁也有代价----> 程序员需要有更高的开销,并且还会降低运行效率

 3.synchronized的使用方式(三种)

synchronized 本质上要修改指定对象的 "对象头". 从使用角度来看, synchronized 也势必要搭配一个具体的对象来使用

什么是对象头?


死锁

1.何为死锁?

在多任务系统下,当一个或多个进程等待系统资源,而资源又被进程本身或其它进程占用时,就形成了死锁

2.死锁出现的场景

(1)一个线程,一把锁             (2)两个线程,两把锁            (3)N个线程,M把锁

(1)一个线程,一把锁

一个线程没有释放锁, 然后又尝试再次加锁
第二次加锁的时候, 就会阻塞等待. 直到第一次的锁被释放, 才能获取到第
二个锁. 但是释放第一个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无
法进行解锁操作. 这时候就会 死锁

(2)两个线程,两把锁

执行程序中两个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源
举一个例子:

(3)N个线程,M把锁

执行程序中多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源

举一个例子--->哲学家就餐问题
每个哲学家会做两件事:思考人生和吃面条
每个哲学家什么时候思考人生,什么时候吃面条是随机的
每个哲学家吃面条的时候,都需要拿起它身边的两根筷子(假设先拿起左手的,后拿起右手的)
每个哲学家都很固执,如果想吃面条的时候,尝试拿筷子,发现筷子被别人占用就会一直等

这个例子发生死锁的时候是--->5个哲学家同时伸出左手,拿起左手的筷子(环路等待)

什么是环路等待?

举一个例子--->等待关系成了环,A等B,B等C,C又等A

话说回来,怎么解决这里出现的死锁问题呢?---->让哲学家拿筷子不是先拿左手,后拿右手,而是先拿编号小的,后拿编号大的.

这种解决方法就是让所有线程都遵守同样的规则顺序,这样就不会出现环路等待了

3.死锁的四个必要条件

实际开发中想要避免死锁,关键点要从避免环路等待切入~

Java标准库

1.线程不安全的类

2.线程安全的类

因为有了锁的控制(synchronized),就可以保证多线程环境下,修改同一个对象不会出现问题
这里 补充一个String类的说明(虽然上面没有提到这个类)
---> String类没有synchronized控制,因为String是不可变对象,单线程都无法改变String,更别提多线程了
既然提到String了,那就再提一嘴
-----> 不可变对象(String)和常量final之间 没有必然联系,不可变对象的意思是,没有提供public的修改属性的操作

volatile关键字

1.volatile能保证内存可见性

前面我们讨论内存可见性时说了, 直接访问工作内存(实际是 CPU 的寄存器或者 CPU 的缓存), 速度
非常快, 但是可能出现数据不一致的情况.加上 volatile , 强制读写内存. 速度是慢了, 但是数据变的更准确了

 

 

2.volatile不保证原子性

  volatile不会引起线程阻塞

wait() 和 notify()

由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.
但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序, 因此就有了 wait()和 notify()
wait和notify都是Object对象的方法,调用wait方法的线程就会陷入阻塞,阻塞到有其他线程通过notify来通知
wait()内部会做三件事:
1.先释放锁
2.等待其他线程的通知
3.收到通知后,重新获取锁,并继续往下执行
看到上面的三件事我们发现,要想想使用wait()/notify(),就要搭配synchronized
我们来看下wait()和notify()是怎么在线程中运作的:

注意:wait()和notify()都是针对同一个对象来操作的

下面这是一个例子来看wait()和notify()的运作顺序:

package thread;

public class Demo18 {
    private static Object locker = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            // 进行 wait
            synchronized (locker) {
                System.out.println("wait 之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait 之后");
            }
        });
        t1.start();

        Thread.sleep(3000);

        Thread t2 = new Thread(() -> {
            // 进行 notify
            synchronized (locker) {
                System.out.println("notify 之前");
                locker.notify();
                System.out.println("notify 之后");
            }
        });
        t2.start();
    }
}

notify()和notifyAll()的区别:

notify()只是唤醒某一个等待线程,notifyAll()可以一次唤醒所有的等待线程
比如举个例子:
不过我们更常用的还是notify()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值