线程的生命周期图
1.新建:创建线程对象
2.就绪:执行start()方法,此时有执行资格,没有执行权
3.运行:有执行资格获得了执行权(线程对象抢到的)
4.死亡:线程对象run()结束或者是通过不安全的方法stop()中断线程,之后线程对象变成垃圾回收
5.阻塞:通过Thread中的sleep()或者是Object类中的wait()方法来休眠线程,使线程没有执行资格,没有执行权,比如说sleep()方法时间到了以后又重新返回来就绪状态,有执行资格,但是执行权还是要去抢的
多线程的实现方案2
实现Runnable接口
实现接口方式的好处
•可以避免由于Java单继承带来的局限性。
•适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
1.先让MyRunable实现Runable接口;
2.重写run()方法;
3.在测试类中创建MyRunable对象
4.在测试类中创建线程对象,通过构造方法实现多线程,如下所示
MyRunable mt = new Myrunable();
Thread thread1 = new Thread(mt, "窗口A");
Thread thread2 = new Thread(mt, "窗口B");
Thread thread3 = new Thread(mt, "窗口C");
5.启用线程对象,使用start()方法
多线程程序练习
需求:
某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
两种方式实现
1.继承Thread类
使用继承Thread类中要把票数设为静态的全局变量才能实现,有代码的耦合性,所以不推荐使用,出现了1张票多卖的情况,以及
票数出现负数的情况
2.实现Runnable接口
使用多线程实现方法2,其中只需要把票数设为全局变量就行,但是还是会出现1张票多卖的情况,以及
票数出现负数的情况,以下是解决方案
解决线程安全问题的基本思想
问题
•相同的票出现多次
•CPU的一次操作必须是原子性的
a=1;这是就是原子性的,但是a++就不是了,a++相当于a=a+1;
•还出现了负数的票
•随机性和延迟导致的
注意
•线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
首先想为什么出现问题?(也是我们判断是否有问题的标准)
•是否是多线程环境?
•是否有共享数据?
•是否有多条语句操作共享数据?
如何解决多线程安全问题呢?
•基本思想:让程序没有安全问题的环境。
•怎么实现呢?
•把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
解决线程安全问题实现1
同步代码块
•格式:
synchronized(对象){需要同步的代码;}
•同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
同步代码块的对象可以是哪些呢?Object()就行
实现代码:
package sellTicket;
public class Ticket implements Runnable {
private int amount = 100;
Object obj = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (obj) {
if(amount==0) {
break;
}
if (amount > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "成功卖出第" + (amount--) + "张票");
//System.out.println(amount);
}
}
}
}
}
同步的前提
•多个线程
•多个线程使用的是同一个锁对象
同步的好处
•同步的出现解决了多线程的安全问题。
同步的弊端
•当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。