黑马程序员——多线程(一)

------- android培训java培训、期待与您交流! ----------

一、创建线程:方式一
 1、定义一个类,从Thread继承
 2、重写run方法
 3、创建线程对象,调用start方法
  start方法启动线程,并执行run方法
  
 (一)为什么要重写run方法呢?
 Thread类是用来描述线程的,而这个线程要执行的代码就需要用一个方法来存储,就是run方法。(主线程执行的代码存放在main方法)
  而Thread类本身的run方法中没有定义任何实现代码,所以只有子类重写run方法,定义自己要实现的内容,线程才有意义。

 (二)run方法和start方法的区别
 run方法直接被调用的时候,是被主线程执行的,和一般的对象调用方法没什么区别;
 而start方法是启动一个线程,然后在新的线程中执行run方法,这时主线程继承执行后面的代码,这就是多线程了。

 (三)线程的四种运行状态:
                                               |---sleep(time)------>冻结(时间到了回到运行)
 被创建----start()---->运行  |---wait()----------->冻结(notify唤醒了继续运行)
                                       |                                        |
                                stop()或                         stop()
                      run方法执行完成                            |
                                      |                                         |
                                    消亡 <-----------------------
    还有一个特殊的状态:临时(堵塞)状态,即具备执行资格,但没有执行权(没有分配到cpu资源)
 堵塞和冻结类似,只是堵塞是被动的,而冻结是主动的(放弃执行资格)

 

二、创建线程:方式二

    多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动。
 当我们需要对一个功能多次执行,并操作同一个数据时,这种方式就不能实现了。
(因为必须要创建多个线程才能多次执行这个功能,而没创建一个线程对象,对象中的数据都是自己特有的)

 而用创建线程的第二种方式就可以实现:实现Runnable接口。
 因为当创建多个线程时,可以把同一对象传递给线程,那么多个线程就可以同时执行对象中的run方法。

 (一)创建线程:方式二:
     1、定义一个类,实现Runnable接口
     2、重写run方法 
     3、创建这个类的对象。
     4、创建一个线程(Thread),并把Runnable接口的实现类的对象作为线程类的构造方法参数传递过去。
     5、start启动线程。由于start方法调用的是Runnable接口的实现类的对象中的run方法,所以这个线程可以多次启动
  
 (三) 实现方式和继承方式的区别:
    1、实现方式避免了单继承的局限性,他可以在继承其他类的同时使用线程。
    2、执行代码存放的位置不同,一个在Runnable接口的实现类的run方法中,一个在Thread类的子类的run方法中。

三、线程安全问题

   当多条语句在操作同一线程共享数据时,在一个线程执行这些语句的空档,另一个线程有可能会进来改变共享数据,导致了共享数据的不同步。

  (一)解决方案:同步
    对操作同一线程共享数据的语句进行加锁,在这些语句执行完之前,不允许其他线程访问锁中的代码。
    同步前提:
    1、存在多个线程(对多个线程同步)
    2、多个线程使用同一把锁
    弊端:多个线程需要判断锁,较为消耗资源

  (二)在使用多线程时,如何找出线程安全问题?
    1、明确哪些代码是多线程运行代码
    2、明确共享数据
    3、明确多线程运行代码中哪些语句是操作共享数据的

四、同步函数

   (一)同步代码块可以找一个共享对象来作为锁,而同步函数用的什么锁呢?
      函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
      而静态同步函数在加载时没有对象,他使用的锁该函数所在类的字节码文件对象(Class对象),类名.class。

   (二)静态同步函数在懒汉式单例设计模式中的应用
 class Single {
     private static Single s = null;
     private Single(){}
     public static Single getInstance(){
         if(s == null){
             synchronized (Single.class) {
                 if(s == null){
                    s = new Single();
                 }
              }
         }
         return s;
     }
}

黑马程序员多线程练习题主要包括两个问题。第一个问题是如何控制四个线程在打印log之前能够同时开始等待1秒钟。一种解决思路是在线程的run方法中调用parseLog方法,并使用Thread.sleep方法让线程等待1秒钟。另一种解决思路是使用线程池,将线程数量固定为4个,并将每个调用parseLog方法的语句封装为一个Runnable对象,然后提交到线程池中。这样可以实现一秒钟打印4行日志,4秒钟打印16条日志的需求。 第二个问题是如何修改代码,使得几个线程调用TestDo.doSome(key, value)方法时,如果传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果。一种解决方法是使用synchronized关键字来实现线程的互斥排队输出。通过给TestDo.doSome方法添加synchronized关键字,可以确保同一时间只有一个线程能够执行该方法,从而实现线程的互斥输出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [黑马程序员——多线程10:多线程相关练习](https://blog.csdn.net/axr1985lazy/article/details/48186039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值