• 多线程 001


    其实java虚拟机jvm不止一个线程,想想执行java程序的是主线程,但垃圾回收机制肯定有一个子线程负责垃圾回收
     
    如何自定义一个线程呢?
    1.创建一个类继承Thread
    2.重写Thread的run方法
            目的:将自定义的代码存储在run方法中,让线程运行
    3.实例化一个线程,调用start方法
        start方法的作用:启动线程;调用run方法
    直接调用t.run方法不行吗,结果是执行完run方法才执行main的,不会出现上面交替的情况
    start和run的区别:start运行(启动)了线程,并执行run方法;而调用run方法,仅仅执行run里的代码
    Demo d1 = new Demo();---->创建一个线程
    d1.start();---->启动线程
    -------------------------------------------------------------------
    体会一下。。。。。
    电脑CPU在快速切换多个进程,而每个进程里面又在不停地切换多个线程。
    run方法用于存储线程运行的代码
    ------------------------------------------------------------------------------
    Thread的五种状态
    也可以具体到6种状态,将冻结状态分为:睡眠状态、等待状态
    当多个线程运行,同一时间只有一个线程能抢到cpu,此时,其它线程就暂时处于阻塞状态;
    调用sleep(时间)方法,可以让一个线程进入冻结状态,当冻结时间结束时,他会进入阻塞状态,抢cpu;
    当调用wait方法时,线程进入冻结状态(等待),调用notify方法,线程重新启动;
    当一个线程被stop(),线程就消亡了,或者程序结束运行,也消亡;
    ---------------------------------------------------------------------------------------
    获取线程对象和名称
     static Thread currentThread(): 获取当前线程的对象(由于是Thread类中的静态方法,所以可以不用实例化直接调用)
    getName():获取线程的名称
    设置线程名称setName()或者构造函数
    -----------------------------------------------------------------------------
    获取线程名称currentThread()
    Thread.currentThread().getName();
    --------------------------------------------------------------------------------
    ------------------------------------------------------------
    设置线程名称
    super(name)-->重写父类的方法,父类有一个改名的构造方法
     
    Test t = new Test("线程名称");
    -------------------------------------------------------------------------
    卖票的小程序
    我一共100张票,3个窗口去卖票,结果卖了300张!每张票都被卖了3次。。。- -!
    解决办法:将票定义为静态的private int ticket = 100;
    但是静态方法生命周期太长了,于是,引出线程的第二种创建方式
    ----------------------------------------------------------------------
    线程的第二种创建方式:实现Runnable接口
    创建一个类实现Runnable接口,然后实现run方法
     
    这次,有四个窗口,只卖了100张票,不会出现重复卖票的情况了
    首先要说明,Runnable不是一个线程,开的四个线程是通过main函数中的实例化t1 2 3 4实现的
    然后,看Ticket类中,输出语句Thread.currentThread().getName()方法,咦,刚开始不是可以省略Thread吗,这次怎么不能了?因为上次类继承了Thread类,所以可以直接调用
    而这次Runnable不是线程,但是currentThread方法是静态的,所以可以通过类名调用
    ----------------------------------------------------------------------
    第二种实现步骤:
    1.定义类实现Runnable接口
    2.覆盖Runnable接口中的run方法
    3.创建Thread对象
    4.强Runnable接口的子类对象作为参数传到Thread类的构造函数中
    5.调用Thread的start方法开启线程,运行Runnable接口子类的run方法
    -----------------------------------------------------------------------
    继承Thread方法和实现Runnable接口的区别
     
    先说说Runnable是怎么来的
    看,第一个student继承Thread,重写了run方法
    但是,第二个学生继承了Person类,java只支持单继承,那么我就没有办法在继承Thread类了,
    那怎么办呢,java工程师就引入Runnable接口,解决这个问题
    我继承了Person类的同时,可以实现Runnable接口,然后实现接口的run方法
    然后,我把这个实现Runnable的子类对象传给Thread(Runnable target)
    调用start方法就可以了
    -----------------------------------------------------------------------------------------------
    多线程的安全问题
    还是那个买票的例子
    想象一下这样的情景:只剩下最后一张票了,当我窗口1判断了if(tick>0),窗口1休眠10毫秒;此时,窗口2开始判断if(tick>0),也休眠10毫秒;窗口3、窗口4都是这样,那么,这四个都符合条件了,等休眠结束后,窗口1取出1号票,此时已经没票了;但是...由于之前窗口2、3、4都符合if语句,那么,他们也会继续执行,结果,就会取出0号票、-1、-2号票,这不就出问题了吗 - -!
     
     
     
    错误的原因:
    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程就参与进来,导致共享数据出现错误。
    ------------------------------------------------------------
    解决办法:
    对多条操作共享数据的语句,只能让一个线程都执行完。执行过程中,其他线程不可以参与进来。
     
    java的解决方式:同步代码块
    synchronized(对象){
    需要被同步的代码
    }
    由于同步代码块需要一个实例对象作为参数,所以我用obj作为参数
    看下结果
    这个同步代码块是怎么玩的呢:
    其实静态代码块就相当于一个开关,默认是开着的,当0线程进去后,它会立马关闭,然后才执行里面的代码;
    执行完毕后,同步代码块才重新打开。
    结合上面代码,里面有个休眠10毫秒,0线程就处于休眠状态;此时,1、2、3线程就开始执行,但执行到同步代码块时,发现它是关闭的,只好阻塞在那里,等0休眠结束后,执行卖票,然后跳出代码块。1、2、3线程就可以进去了,但是此时,进去后判断ticket已经==0了,不成立了,就卖不出-1号票了。
     
    举个生活实例:火车上的厕所,无人才能进去,进去就上锁,此时第二个人不能进去
    ----------------------------------------------------------------------------------------------
    同步的前提:
    1.必须两个或者两个以上的线程
    2.必须是多个线程使用同一个锁
    3.必须保证同步中只能有一个线程在执行
     
    好处:解决了多线程的安全问题
    弊端:多个线程都判断这个锁,较为消耗资源
     
    如何找问题:
    1.明确那些代码是多线程运行的代码
    2.明确共享数据
    3.明确多线程运行中哪些语句是操作共享数据的
    ---------------------------------------------------------------
    同步函数
    think:函数也具备封装代码的功能,同步代码块也是封装代码,多出来一条就是同步性,那么,能不能搞个同步函数呢
    synchronized用来修饰函数就行了
    public synchronized void add(int n){
    }
    那么,就可以把同步代码块中的数据封装成同步函数了
    那么,同步函数到底是谁的锁呢,答案是this的
    我定义一个boolean型deal,注意,同步代码块中的synchronized参数是this,而交替执行同步方法和同步代码块,并没有出现安全问题,也就是说,这两个操作的是同一个锁,即,同步函数的是this的锁。
     
    --------------------------------------------------------------
    如果同步函数被静态修饰,使用的锁不再是this的了,
    因为,静态函数在方法区,不可能定义this,
    静态进内存的时候内存中没有本类对象对象,但是有该类的字节码文件.class
    所以,静态同步函数的对象时字节码文件对象---->类名.class
    ------------------------------------------------------------------
    同步的问题
    死锁
    同步代码块中调用同步函数,而同步函数中也调用同步代码块,就可能产生死锁
     
     
     
  • 相关阅读:
    线程池ThreadPoolExecutor
    常用引擎+存储过程
    在浏览器中输入www.baidu.com后执行的全过程
    win端git连接私服仓库+上传本地项目+从服务器下载文件到win
    TCP的三次握手和四次挥手+TCP和UDP的区别
    2017网易---计算糖果
    ubuntu下wireshark+scapy+tcpreply
    网易2017---数列还原
    2017网易---藏宝图
    2017网易---不要二
  • 原文地址:https://www.cnblogs.com/aisi-liu/p/4227628.html
Copyright © 2020-2023  润新知