java多线程实例图解讲解

java多线程实例图解讲解

  1. I     总结
    1. 一进程与多线程
    2. 二线程的创建
    3. 三多线程的安全问题
    4. 四多线程运行状态
    5. 五获取线程对象以及名称
    6. 六  售票程序
    7. 七同步函数
  2. I.     总结

    java多线程实例讲解,今天对多线程有了初步的了解,知道了进程与线程的区别,以及线程存在的意义。线程是如何创建、如何运行,多线程的安全问题。之后学习了同步函数以及同步函数的“锁”、与同步有关的延迟加载的单例设计模式(懒汉式),还学习了多线程的死锁问题。

    一、进程与多线程

    进程是正在进行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫控制单元。

    线程是进程中一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程。Java虚拟机启动时会有一个java.exe进程,该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。(jvm启动不知一个线程,还有负责垃圾回收机制的线程)。

    多线程存在的意义:

    让程序中的多部分代码同时执行,能帮我们调高效率。

    多线程的特性:
    某一时刻,只能有一个程序在运行(多核除外),cpu做着快速的切换,以达到看上去是同时运行的效果。多线程运行行为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。

    二、线程的创建

    线程的创建

    1、继承Thread类

    Java提供了对线程这类事物的描述——Thread类。

    a.     定义类继承Thread;

    b.     复写Thread类中的run()方法。

    目的:将自定义代码块存储在run方法中。

    Tread类中定义了一个run方法,用于存储线程要运行的代码块。就相当于主线程代码存放在main中。

    c.      调用线程的start方法

    start:启动线程——–à调用run方法

     

    开启线程的目的是为了运行指定代码,父类提供了空间,只需要沿袭父类功能,复写父类内容即可。因此,需要继承Thread,把run()的内容复写掉,建立线程要运行的代码。

    2、声明实现Runnable接口的类

    A.定义类,实现Runnable接口

     

     

    [java] view plaincopy

    1. {
    2.        private int tick = 100;
    3.        public void run()
    4.        {
    5.               while(true)
    6.               {
    7.                      if (tick > 0)
    8.                      {
    9. System.out.println(Thread.currentThread().getName()+”——-sale:”+tick–);
    10. }
    11.               }
    12.        }
    13. }

     

     

    B. 覆盖Runnable接口中的run方法

    线程代码存放在接口的子类的run方法中

    C. 通过Thread类建立线程对象

    Ticket  t = new Ticket();

    D.将Runnable接口的子类对象(即刚刚new的Ticket())作为实际参数传递给Thread类的构造函数

    Thread t1 = new Thread(t);

    Thread t2 = new Thread(t);

    Thread t3 = new Thread(t);

    Thread t4 = new Thread(t);

     

    自定义的run方法所属的对象是Runnable接口的子类对象。因此,要让线程去指定的

    象的run方法。必须明确run方法所属的对象。

    E. 调用Thread类的start方法开启线程并调用Runnable接口的run方法。

    t1.start();

    t2.start();

    t3.start();

    t4.start();

    三、多线程的安全问题

    安全问题原因:
    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,

    还未执行完,另一个线程参与进来执行,导致共享数据错误。

    解决办法

    对多条【操作共享数据】的语句,一个线程在执行时,其他线程不可以参与执行,只能让一个线程先执行完。

    Java对于多线程的安全问题提供了专业的解决方式——同步代码块。

          

    synchronized()

           {

    }

     

    对象如同,持有锁的线程才可以在同步中执行。没有持有锁的线程,即使获取cpu的权限,也无法进入同步代码块执行。

    同步的前提:

    Ø  必须要有两个或以上的线程。

    Ø  必须是多个线程使用同一个锁。

    必须保证同步中只能有一个线程

    同步的好处:解决了多线程的安全问题。

    同步的弊端:消耗了资源——在执行代码以外多了一个判断锁的过程(每次都判断)

    [java] view plaincopy

    1. <pre name=”code” class=”java”><p></p><h2><a name=”t4″></a>四、多线程运行状态</h2><div><img src=”http://img.blog.csdn.net/20130701182320609?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ29uZ3Nob3VkYW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center” alt=”" data-pinit=”registered”>
    2. </div><div><h2><a name=”t5″></a>五、获取线程对象以及名称</h2><p>线程有自己的(默认)名称:Thread-编号    该编号从0开始。</p><img src=”http://img.blog.csdn.net/20130701182422062?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ29uZ3Nob3VkYW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center” alt=”">
    3. </div><div>
    4. </div><p></p><p>currentThread()返回当前线程对象。currentThread()方法是静态的,说明它里面没有访问到特有对象数据。</p><p>static Thread currentThread():获取当前线程对象。</p><p>getName():获取线程名称。</p>设置线程名称:setName或者构造函数。(设置名称的意义:获取当前运行线程的名称,并进行判断)
    5. <p><img src=”http://img.blog.csdn.net/20130701182511796?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ29uZ3Nob3VkYW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center” alt=”" data-pinit=”registered”>
    6. </p><p></p><p><strong>注:</strong>线程1和线程2里的x不是同一个。</p><p>l  Thread-0进来,栈内存会给Thread-0分配内存空间–à此空间里有一个run()方法,run()方法里有一个x;</p><p>l  Thread-1进来,栈内存给Thread-1分配内存空间–à此空间也有一个run方法,run方法里也有一个x。</p><h2><a name=”t6″></a>六、  售票程序</h2>创建一个对象,里面都有100张票–>
    7. <span style=”white-space:pre”><img src=”http://img.blog.csdn.net/20130701182617484?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ29uZ3Nob3VkYW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center” alt=”" data-pinit=”registered”><span style=”white-space:pre”></span></span></pre>
    8. <pre></pre>

    这样设计,会产生同号票。

    因此让4个对象共享100张票—>静态static

    但是,一般不定义静态,因为生命周期太长。

     

    于是,将代码改为:

    但是会提示无效线程状态异常:

    已经在运行的程序,不需要再次开启。

    解决此问题,就需要实现Runnable接口

     

    七、同步函数

    publicsynchronized void add()

    {

    }

     

    1、明确哪些代码是多线程运行代码。

    2、明确共享数据。

    3、明确多线程运行代码中哪些语句是操作共享数据的。

     

    l  同步函数的锁:

    函数需要被对象调用,那么函数都有一个所属对象的引用。就是this。

    所以同步函数使用的锁是this。

    静态同步函数的锁是Class对象,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

    类名.Class   该对象的类型是Class

     

    静态的同步方法,使用的锁是该方法所在类的字节码文件对象。

     

    l  单例设计模式——懒汉式

    懒汉式的特点在于实例的延迟加载,

    问题:多线程访问时会存在安全问题。

    解决办法:可以加同步来解决,加同步的方式:同步代码块、同步函数(有

    些低效)

    用双重判断的形式能解决效率问题。

    加同步时使用的锁是:该类所属的字节码文件对象(如,Single.class)。

    面试:延迟加载的单例设计模式示例

    懒汉式

     

    [java] view plaincopy

    1. class Single
    2. {
    3.        Privatestatic Single s = null;
    4.        Private  Single(){}
    5.        Publicstatic Single getInstance()
    6. <span style=”white-space:pre”>  </span>{
    7.        <span style=”white-space:pre”>   </span><span style=”white-space:pre”>   </span>If(s==null)
    8.       <span style=”white-space:pre”>        </span> {
    9.            <span style=”white-space:pre”>   </span>   Synchronized(Single.class)
    10. <span style=”white-space:pre”>      </span>{
    11.       <span style=”white-space:pre”>        </span> If(s==null)
    12. <span style=”white-space:pre”>      </span>{
    13.      <span style=”white-space:pre”>     </span>  S = new Single();
    14. <span style=”white-space:pre”>      </span>}
    15. <span style=”white-space:pre”>  </span>}
    16. }
    17. Return s;
    18. }
    19. }

     

     

    l  死锁

    有备无患,防止面试时让写一个死锁程序。

本文链接地址: java多线程实例图解讲解