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


一、概述

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


   2、线程:进程中的一个独立的控制单元,线程在控制着进程的执行。

如:

   Java VM 启动时会有一个进程的java.exe,该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程这被称之为主线程。其实它又不止一个线程,还有负责垃圾回收机制的线程。


二、创建线程

1、继承Thread类。


2、复写Thread类中run方法。

   复写原因:在API中可参考到,Thread类是用于描述线程的,其内部就定义了一个功能,即用于存储线程要运行的代码,该存储功能就是run方法,如果不复写,则run方法的初始值就为run(){},换句话说,Thread类中的run方法,用存储线程要运行的代码。


3、调用线程的start方法,该方法有两个作用:启动线程、调用run方法。

class MyThread extends Thread
{
    public void run()
    {
        //为了看到打打印效果,可以写个for循环。
        for(int = 0; x < 200; x++)
        {
            System.out.println("MyThread:"x);
        }
    }
}
class ThreadTest
{
    public static void main(String[] args)
    {
        //创建一个线程
        MyThread mt = new MyThread();
        //开启并执行已经创建好的线程的run方法
        mt.start();
        //仅仅是对象调用方法,而线程创建了,并没有运行。
        //mt.run();
        for(int x = 0; x < 200; x++)
        {
            System.out.println("main:"+x);
        }
    }
    /*
    发现运行结果每次都不同,因为多个线程都获取cpu的执行权,cpu
    执行到谁,谁就运行。明确一点,在某一时刻,只能有一个程序运行
    。(多核除外),而实际上CPU是在做着快速的切换,以达到看上去是同
    时运行的效果,我们可以形象把多线程的运行行为在互相抢夺CPU的执
    行权,这就是多线程的特性:随机性,由CPU决定执行和执行的时长。
    */
}


三、线程运行状态

   请参考以下图例:

黑马程序员-Java基础知识预备之Java多线程_多线程


四、获取线程对象以及名称

1、Thread-编号,该编号从0开始


2、Thread类中提供了如下一些方法:

  • 静态方法:currentThread():获取当前线程对象。

  • 对象方法:getName():获取线程名称。

  • 设置线程名称:setName()或者构造函数(具体参阅API文档)。


五、创建线程另一种方式--实现Runnable接口

1、定义类实现Runnable接口


2、覆盖Runnable接口中的run方法。将线程要运行的代码存入在该run方法中。


3、通过Thread类建立线程对象。


4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。


5、思考:

   为什么要将Runnable接口的子类对象传递给Thread的构造函数?

因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。


6、调用Thread类的start方法,开启线程并调用Runnable接口子类的run方法。

class Ticket implements Runnable
{
    private static int ticket = 100;
    //重写该接口的run方法。
    public void run()
    {
        while(ticket > 0)
        {
            /*
            当前线程类实现Runnable接口时,如果想获取当前线程,只能用
            Thread.currentThread()方法。
            */
            System.out.println(Thread.currentThread().getName()+"----sale----"+ticket--);
        }
    }
}
//测试类
class TreadTest2
{
    public static void main(String[] args)
    {
        //创建一个实现了Runnable接口的Ticket实例
        Ticket t = new Ticket();
        //创建线程,并关联对象
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        //开启线程
        t1.start();
        t2.start();
        /*
        通过匿名对象的方式,可以把上面简化成如下代码:
        new Thread(t,"可以指定线程名称1").start();
        new Thread(t,"可以指定线程名称2").start();
        */
    }
}


六、实现方式和继承方式有什么区别呢?(面试常题)

1、实现方式好处:避免了单继承的局限性。所以在定义线程时,建议使用实现方式。


2、继承Thread:线程代码存放在Thread的子类run方法中。

  实现Runnable:线程代码存放在接口的子类的run方法中。


七、线程的安全--同步代码

1、原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没

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


2、解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不

可以参与执行。


3、Java中对于线程的安全问题提供了专业的解决方式:同步代码

格式:synchronized(对象)

{

需要被同步的代码

}


4、同步的前提

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

②必须是多个线程共享一个锁。


5、保证同步中只能有一个线程在运行

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

  • 弊端:多个线程需要判断锁,较为消耗资源。


八、线程的安全--同步函数

1、三个明确:

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

②明确共享数据。

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


2、同步有两种表现形式:

①同步代码块:需要定义对象,多了一层缩进。

Object obj = new Object();
public void method(parameterType pt)
{
    synchronized(obj)
    {
        //被操作的共享数据
    }
}

②同步函数:写法要比同步代码块要好些。

public synchronized void method(parameterType pt)
{
    //被操作的共享数据
}

3、同步函数用的锁

①函数需要被对象调用,那么函数都有一个所属对象引用 ,就是this,所以同步函数使用的锁是this

②如果同步函数被静态修饰后,使用的锁又是什么呢?

  • 被静态修饰后,内存中没有本类对象,但是一定有该类对应的字节码文件对象,所以静态的同步方法,使用的锁是该方法所在字节码文件对象,即:类名.class(在内在中是唯一的,因为字节码文件是唯一的);


4、关于懒汉式和饿汉式

①懒汉式和饿汉式有什么不同?

  • 懒汉式的特点是实例的延时加载。

②懒汉式有没有问题?

  • 有,如果多线程并发访问时,会出现安全隐患。

③怎么解决?

  • 可以加同步来解决,加同步的方式有两种方式:同步函数,同步代码块,但是稍微有点低效。可用双重判断的形式可以解决效率上的问题。

④加同步的时使用的锁是哪一个?

  • 该类所属的字节码文件对象。


九、死锁

当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。


十、传统的线程通信

1、线程间通信,其实就是多个线程在操作同一个资源。但是操作的动作不同。


2、对于使用synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器。所以可以在同步方法中直接调用以下3个方法。

   对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里

的对象,所以必须使用该对象调用这以下3个方法。

①wait():导致当前线程等待,直到其线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程。调用wait()方法的当前线程会释放对该同步监视器的锁定。

②notify():唤醒在此同步监视器上等待的单个线程(如果有多个,会唤醒任意中的一个)。

只有当前线程放弃对同步监视器的锁定后(使用wait()方法),其它被唤醒的线程才可以执行。

③唤醒在此同步监视器上等待的所有线程。只有当前线程放弃对同步监视器的锁定后(使用wait()方法),其它被唤醒的线程才可以执行。


3、思考:为什么这些操作线程的方法被定义在Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程才可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。


十一、使用Condition控制线程通信

  • JDK1.5中提供了多线程升级解决方案:

   将同步synchronized替换成现实的Lock操作。

  • await;sinal();signalAll();


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