您的位置:首页 > 职场人生

黑马程序员—多线程

2013-03-24 12:47 204 查看
------- android培训java培训、期待与您交流!
----------

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

线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。

一个进程中至少有一个线程。

Java VM 启动的时候会有一个进程java.exe.

该进程中至少一个线程负责java程序的执行。

而且这个线程运行的代码存在于main方法中。

该线程称之为主线程。

扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

创建线程方式一

步骤:

1,定义类继承Thread。

2,复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。

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

因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。

明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)

cpu在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

为什么要覆盖run方法呢?

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。

也就是说Thread类中的run方法,用于存储线程要运行的代码。

线程的四种状态:



练习1:创建两个线程,和主线程交替运行



创建线程方式二

步骤:

1,定义类实现Runnable接口

2,覆盖Runnable接口中的run方法。

将线程要运行的代码存放在该run方法中。

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

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

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

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

因为自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。

实现方式和继承方式有什么区别呢?

实现方式好处:避免了单继承的局限性。

在定义线程时,建立使用实现方式。

两种方式区别:

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

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

线程安全问题

导致安全问题的出现的原因:

1,多个线程访问出现延迟。

2,线程随机性。

注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

同步(synchronized)

格式:

synchronized(对象)

{

需要同步的代码;

}

同步可以解决安全问题的根本原因就在那个对象上。

该对象如同锁的功能。

同步的特点

同步的前提:

1,同步需要两个或者两个以上的线程。

2,多个线程使用的是同一个锁。

未满足这两个条件,不能称其为同步。

同步的弊端:

当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

同步代码块练习:用户往银行存钱





同步函数

格式:在函数上加上synchronized修饰符即可。

同步函数用的是哪一个锁呢?

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

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

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

通过验证,发现不在是this。因为静态方法中也不可以定义this。

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

类名.class 该对象的类型是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;

}

}

同步死锁:同步中嵌套同步



线程间通信



wait():将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。

notify():唤醒线程池中某一个等待线程。

notifyAll():唤醒的是线程池中的所有线程。

思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

1,这些方法存在与同步中。

2,使用这些方法时必须要标识所属的同步的锁。

3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

思考2:wait(),sleep()有什么区别?

wait():释放cpu执行权,释放锁。

sleep():释放cpu执行权,不释放锁。

练习1:使用同一个资源



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

将同步Synchronized替换成了Lock。

将Object中的wait,notify notifyAll,替换成了Condition对象。

该对象可以Lock锁 进行获取。

该示例中,实现了本方只唤醒对方操作。

Lock:替代了Synchronized

lock

unlock

newCondition()

Condition:替代了Object wait notify notifyAll

await();

signal();

signalAll();

总结:Lock锁可以对应多个condition对象,避免了死锁的出现。

练习2:生产者与消费者



停止线程

interrupt():中断线程。

1,定义循环结束标记

因为线程运行代码一般都是循环,只要控制了循环即可。

2,使用interrupt(中断)方法。

该方法是结束线程的冻结状态,使线程回到运行状态中来。

注:stop方法已经过时不再使用。

线程类的其他方法

setPriority(int newPriority):更改线程的优先级。

getPriority():返回线程的优先级。

toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。

setDaemon(true):将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

join:临时加入一个线程的时候可以使用join方法。

当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。

------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: