多线程编程 基础篇 (四)

基础篇(四)

    [wait(),notify()/notityAll()方法]

    关于这两个方法,有很多的内容需要说明.在下面的说明中可能会有很多地方不能一下子
明白,但在看完本节后,即使不能完全明白,你也一定要回过头来记住下面的两句话:

[wait(),notify()/notityAll()方法是普通对象的方法(Object超类中实现),而不是线程对象的方法]

[wait(),notify()/notityAll()方法只能在同步方法中调用]


    [线程的互斥控制]
    
    多个线程同时操作某一对象时,一个线程对该对象的操作可能会改变其状态,而该状态会影响
另一线程对该对象的真正结果.
    这个例子我们在太多的文档中可以看到,就象两个操售票员同时售出同一张票一样.

    线程A                                        线程B

1.线程A在数据库中查询存票,发现票C可以卖出
2.线程A接受用户订票请求,准备出票.
3.                                                  这时切换到了线程B执行
4.                                                  线程B在数据库中查询存票,发现票C可以卖出                        
5.                                                  线程B将票卖了出去  
6.切换到线程A执行,线程A卖了一张已经卖出的票

所以需要一种机制来管理这类问题的发生,当某个线程正在执行一个不可分割的部分时,其它线程不能
不能同时执行这一部分.

象这种控制某一时刻只能有一个线程执行某个执行单元的机制就叫互斥控制或共享互斥(mutual exclusion)

    在JAVA中,用synchronized关键字来实现互斥控制(暂时这样认为,JDK1.5已经发展了新的机制)

    [synchornized关键字]
    把一个单元声明为synchronized,就可以让在同一时间只有一个线程操作该方法.
    
    有人说synchornized就是一把锁,事实上它确实存在锁,但是是谁的锁,锁谁,这是一个非常复杂的
问题.

    每个对象只有一把监视锁(monitor lock),一次只能被一个线程获取.当一个线程获取了这一个锁
后,其它线程就只能等待这个线程释放锁才能再获取.

    那么synchronized关键字到底锁什么?得到了谁的锁?
    
    对于同步块,synchronized获取的是参数中的对象锁:

    synchronized(obj){
        //...............
    }
    线程执行到这里时,首先要获取obj这个实例的锁,如果没有获取到线程只能等待.如果多个线程
执行到这里,只能有一个线程获取obj的锁,然后执行{}中的语句,所以,obj对象的作用范围不同,控制程序
不同.
    
    假如:
    public void test(){
        Object o = new Object();
        
        synchronized(obj){
            //...............
        }
    }

    这段程序控制不了任何,多个线程之间执行到Object o = new Object();时会各自产生一个对象
然后获取这个对象有监视锁,各自皆大欢喜地执行.

    而如果是类的属性:

    class Test{
        Object o = new Object();
        public void test(){

            synchronized(o){
                //...............
            }
        }
    }

    所有执行到Test实例的synchronized(o)的线程,只有一个线程可以获取到监视锁.

    有时我们会这样:
        public void test(){

            synchronized(this){
                //...............
            }
        }
    那么所有执行Test实例的线程只能有一个线程执行.而synchornized(o)和
synchronized(this)的范围是不同的,因为执行到Test实例的synchornized(o)的线程等待时,其它线程可以执行
Test实例的synchronized(o1)部分,但多个线程同时只有一个可以执行Test实例的synchornized(this).
    
    而对于    synchronized(Test.class){
                //...............
        }这样的同步块而言,所有调用Test多个实例的线程赐教只能有一个线程可以执行.


    

    [synchronized方法]

    如果一个方法声明为synchronized的,则等同于把在为个方法上调用synchronized(this).

    如果一个静态方法被声明为synchronized,则等同于把在为个方法上调用synchronized(类.class).

    




现在进入wait方法和notify/notifyAll方法.这两个(或叫三个)方法都是Object对象的方法,而不是
线程对象的方法.如同锁一样,它们是在线程中调用某一对象上执行的.

    class Test{
        public synchronized void test(){
        //获取条件,int x 要求大于100;
        
            if(x < 100)
                wait();
        }
    }

    这里为了说明方法没有加在try{}catch(){}中,如果没有明确在哪个对象上调用wait()方法,则
为this.wait();
    假如:

        Test t = new Test();
    现在有两个线程都执行到t.test();方法.其中线程A获取了t的对象锁,进入test()方法内.
    这时x小于100,所以线程A进入等待.

    当一个线程调用了wait方法后,这个线程就进入了这个对象的休息室(waitset),这是一个虚拟的
对象,但JVM中一定存在这样的一个数据结构用来记录当前对象中有哪些程线程在等待.
    当一个线程进入等待时,它就会释放锁,让其它线程来获取这个锁.

    所以线程B有机会获得了线程A释放的锁,进入test()方法,如果这时x还是小于100,线程B也进入了
t的休息室.
    这两个线程只能等待其它线程调用notity[All]来唤醒.

    但是如果调用的是有参数的wait(time)方法,则线程A,B都会在休息室中等待这个时间后自动唤醒.


    [为什么真正的应用都是用while(条件)而不用if(条件)]

    在实际的编程中我们看到大量的例子都是用        
            while(x < 100)
                wait();go();而不是用if,为什么呢?
    在多个线程同时执行时,if(x <100)是不安全的.因为如果线程A和线程B都在t的休息室中等待,
这时另一个线程使x==100了,并调用notifyAll方法,线程A继续执行下面的go().而它执行完成后,x有可能
又小于100,比如下面的程序中调用了--x,这时切换到线程B,线程B没有继续判断,直接执行go();就产生一个
错误的条件,只有while才能保证线程B又继续检查一次.
    

    [notify/notifyAll方法]
    这两个方法都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个,但究竟是哪一个不能确
定,而notifyAll则唤醒这个对象上的休息室中所有的线程.


    一般有为了安全性,我们在绝对多数时候应该使用notifiAll(),除非你明确知道只唤醒其中的一个
线程.

    那么是否是只要调用一个对象的wait()方法,当前线程就进入了这个对象的休息室呢?事实中,要调
用一个对象的wait()方法,只有当前线程获取了这个对象的锁,换句话说一定要在这个对象的同步方法或
以这个对象为参数的同步块中.
    

class MyThread extends Thread{
  Test t = new Test();
    public void run(){
      t.test();
        System.out.println("Thread say:Hello,World!");
    }
}


public class Test {
  
    int x = 0;
    public  void test(){
      if(x==0)
        try{
          wait();
        }catch(Exception e){}
    }
    public static void main(String[] args) throws Exception{
      new MyThread().start();
    }
}

这个线程就不会进入t的wait方法而直接打印出Thread say:Hello,World!.
而如果改成:


public class Test {
  
    int x = 0;
    public synchronized void test(){
      if(x==0)
        try{
          wait();
        }catch(Exception e){}
    }
    public static void main(String[] args) throws Exception{
      new MyThread().start();
    }
}
我们就可以看到线程一直等待,注意这个线程进入等待后没有其它线程唤醒,除非强行退出
JVM环境,否则它一直等待.


所以请记住:
[线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又立即
释放该锁]

以上是对线程基础知识的简单介绍,不进入实例,我们无法真正了解它的真实意义.下节我们就
会以实例来进入多线程编程的 实战篇

[JAVA工程师必会知识点之并发编程] 1、现在几乎100%的公司面试都必须面试并发编程,尤其是互联网公司,对于并发编程的要求更高,并发编程能力已经成为职场敲门砖。 2、现在已经是移动互联和大数据时代,对于应用程序的性能、处理能力、处理时效性要求更高了,传统的串行化编程无法充分利用现有的服务器性能。 3、并发编程是几乎所有框架的底层基础,掌握好并发编程更有利于我们学习各种框架。想要让自己的程序执行、接口响应、批处理效率更高,必须使用并发编程。 4、并发编程是中高级程序员的标配,是拿高薪的必备条件。 【优惠说明】 1、120余节视频课,原价299元,今日报名立减100,仅需199元 2、现在购课,就送价值800元的编程大礼包! 备注:请添加微信:itxy41,按提示获取讲师答疑服务。 【主讲讲师】 尹洪亮Kevin: 现任职某互联网公司首席架构师,负责系统架构、项目群管理、产品研发工作。 10余年软件行业经验,具有数百个线上项目实战经验。 擅长JAVA技术栈、高并发高可用伸缩式微服务架构、DevOps。 主导研发的蜂巢微服务架构已经成功支撑数百个微服务稳定运行 【推荐你学习这门课的理由:知识体系完整+丰富学习资料】 1、 本课程总计122课时,由五大体系组成,目的是让你一次性搞定并发编程。分别是并发编程基础、进阶、精通篇、Disruptor高并发框架、RateLimiter高并发访问限流吗,BAT员工也在学。 2、课程附带附带3个项目源码,几百个课程示例,5个高清PDF课件。 3、本课程0基础入门,从进程、线程、JVM开始讲起,每一个章节只专注于一个知识点,每个章节均有代码实例。 【课程分为基础篇、进阶篇、高级篇】 一、基础基础篇从进程与线程、内存、CPU时间片轮训讲起,包含线程的3种创建方法、可视化观察线程、join、sleep、yield、interrupt,Synchronized、重入锁、对象锁、类锁、wait、notify、线程上下文切换、守护线程、阻塞式安全队列等内容。 二、进阶篇 进阶篇课程涵盖volatied关键字、Actomic类、可见性、原子性、ThreadLocal、Unsafe底层、同步类容器、并发类容器、5种并发队列、COW容器、InheritableThreadLocal源码解析等内容。 三、精通篇 精通篇课程涵盖JUC下的核心工具类,CountDownLath、CyclicBarrier、Phaser、Semaphore、Exchanger、ReentrantLock、ReentrantReadWriteLock、StampedLock、LockSupport、AQS底层、悲观锁、乐观锁、自旋锁、公平锁、非公平锁、排它锁、共享锁、重入锁、线程池、CachedThreadPool、FixedThreadPool、ScheduledThreadPool、SingleThreadExecutor、自定义线程池、ThreadFactory、线程池切面编程、线程池动态管理等内容,高并发设计模式,Future模式、Master Worker模式、CompletionService、ForkJoin等 课程中还包含 Disruptor高并发无锁框架讲解:Disruptor支持每秒600万订单处理的恐怖能力。深入到底层原理和开发模式,让你又懂又会用。 高并发访问限流讲解:涵盖木桶算法、令牌桶算法、Google RateLimiter限流开发、Apache JMeter压力测试实战。 【学完后我将达到什么水平?】 1、 吊打一切并发编程相关的笔试题、面试题。 2、 重构自己并发编程的体系知识,不再谈并发色变。 3、 精准掌握JAVA各种并发工具类、方法、关键字的原理和使用。 4、 轻松上手写出更高效、更优雅的并发程序,在工作中能够提出更多的解决方案。 【面向人群】 1、 总感觉并发编程很难、很复杂、不敢学习的人群。 2、 准备跳槽、找工作、拿高薪的程序员。 3、 希望提高自己的编程能力,开发出更高效、性能更强劲系统的人群。 4、 想要快速、系统化、精准掌握并发编程的人群。 【课程知识体系图】
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页