java多线程实例图解讲解
- I 总结
- 一进程与多线程
- 二线程的创建
- 三多线程的安全问题
- 四多线程运行状态
- 五获取线程对象以及名称
- 六 售票程序
- 七同步函数
-
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- {
- private int tick = 100;
- public void run()
- {
- while(true)
- {
- if (tick > 0)
- {
- System.out.println(Thread.currentThread().getName()+”——-sale:”+tick–);
- }
- }
- }
- }
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- <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”>
- </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=”">
- </div><div>
- </div><p></p><p>currentThread()返回当前线程对象。currentThread()方法是静态的,说明它里面没有访问到特有对象数据。</p><p>static Thread currentThread():获取当前线程对象。</p><p>getName():获取线程名称。</p>设置线程名称:setName或者构造函数。(设置名称的意义:获取当前运行线程的名称,并进行判断)
- <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”>
- </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张票–>
- <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>
- <pre></pre>
这样设计,会产生同号票。
因此让4个对象共享100张票—>静态static
但是,一般不定义静态,因为生命周期太长。
于是,将代码改为:
但是会提示无效线程状态异常:
已经在运行的程序,不需要再次开启。
解决此问题,就需要实现Runnable接口
七、同步函数
publicsynchronized void add()
{
}
1、明确哪些代码是多线程运行代码。
2、明确共享数据。
3、明确多线程运行代码中哪些语句是操作共享数据的。
l 同步函数的锁:
函数需要被对象调用,那么函数都有一个所属对象的引用。就是this。
所以同步函数使用的锁是this。
静态同步函数的锁是Class对象,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.Class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。
l 单例设计模式——懒汉式
懒汉式的特点在于实例的延迟加载,
问题:多线程访问时会存在安全问题。
解决办法:可以加同步来解决,加同步的方式:同步代码块、同步函数(有
些低效)
用双重判断的形式能解决效率问题。
加同步时使用的锁是:该类所属的字节码文件对象(如,Single.class)。
面试:延迟加载的单例设计模式示例
懒汉式
[java] view plaincopy- class Single
- {
- Privatestatic Single s = null;
- Private Single(){}
- Publicstatic Single getInstance()
- <span style=”white-space:pre”> </span>{
- <span style=”white-space:pre”> </span><span style=”white-space:pre”> </span>If(s==null)
- <span style=”white-space:pre”> </span> {
- <span style=”white-space:pre”> </span> Synchronized(Single.class)
- <span style=”white-space:pre”> </span>{
- <span style=”white-space:pre”> </span> If(s==null)
- <span style=”white-space:pre”> </span>{
- <span style=”white-space:pre”> </span> S = new Single();
- <span style=”white-space:pre”> </span>}
- <span style=”white-space:pre”> </span>}
- }
- Return s;
- }
- }
l 死锁
有备无患,防止面试时让写一个死锁程序。