·Background
一提到线程的概念首先应该想到进程。
简单说来,进程就是正在运行的程序;换句话说进程是系统进行资源分配和调用的独立单位。每个进程都有自己的内存空间和系统资源。
·线程
线程是程序中一个单一的顺序控制流程。线程是进程中的一个实体,是被系统独立调度和分派的基本单位。
·多线程
在单个程序中同时运行多个线程完成不同的工作,称为多线程。
需要注意的一点是,多线程的存在,不是提高程序的执行速度,而是为了提高应用程序的使用率。
·多线程的实现方式
方式一:继承Thread类
1)自定义MyThread类继承Thread类
2)在MyThread类中继承run()方法
3)创建MyThread类的对象
4)使用start()方法启动对象
方式二:实现Runnable接口
1)自定义MyRunnable类实现Runnable接口
2)在MyRunnable类中重写run()方法
3)创建MyRunnable类的对象
4)创建Thread类的对象,并把上一步创建的对象作为构造函数的参数传递过来。
同方式一相比,方式二的优势:
1)可以避免由于java单继承带来的局限性
2)适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效的分离,较好的体现了面向对象的设计思想。
·多线程的安全问题:
需要从以下三个方面去考虑:
1)是否有多线程环境。
2)是否有共享数据
3)是否是多条语句操作共享数据
·线程中的死锁问题
理解:两个或两个以上的线程在执行的过程中争夺资源容易发生死锁的情况。
原因:同步嵌套情况下容易出现。
·线程的生命周期
主要有两种:
1)新建-就绪-运行-死亡
2)新建-就绪-运行-阻塞-就绪-运行-死亡
·常用方法
1)yield()
线程让步也就是暂停当前正在执行的线程对象,并执行指定的线程对象
2)stop()
线程中断。可以设定一定的休息时间,限定时间内无醒不过来就可以直接stop进行线程中断。
3)sleep()
线程休眠。可以设置一定的休息时间。
4)priority()
线程优先级调度。线程默认的优先级为5(优先级范围为:1-10)
5)join()
线程加入。
6)daemon()
守护线程。
7)start()
开启线程。
8)run()
运行线程
当然,还有很多其他的方法也是经常用到的,比如:getThread(),setThread()等。这里就不一一列举啦。
下面是小编模仿做的一个小实例:
1)场景介绍:某电影院目前正在上映的贺岁大片,共有100张票,且它有三个售票窗口,设计一个程序模拟该电影院的售票。
2)实现思路:采用继承Runnable接口的方式实现。代码如下:
---定义sellTicket类---
public class SellTicket implements Runnable{
//定义100张票(为了让多个线程共享。。)
private static int tickets=100;
@Override
public void run(){
//是为了模拟一直有票
while(true){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
---SellTicketDemo类---
public class SellTicketDemo {
public static void main(String[] args) {
//创建资源对象
SellTicket st=new SellTicket();
//创建三个线程对象
Thread t1=new Thread(st,"窗口1");
Thread t2=new Thread(st,"窗口2");
Thread t3=new Thread(st,"窗口3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
3)执行效果:
4)出现的问题:
问题1:相同的票被卖出多次
问题2:出现负数和0票。
5)解决思路
很显然,需要保证线程的安全性。问题就在于最一开始初始化100张票那。100英爱作为多个线程共享的数据。所以需要保证其原子性。另一方面就是需要为线程加锁(设置一定的延迟时间)
且看小编改进后的SellTicket类的代码实现:
public class SellTicket implements Runnable{
//定义100张票
private int tickets=100;
//定义一把锁
private Object obj=new Object();
@Override
public void run() {
while(true){
synchronized(obj){//发现这里的代码将来是会被锁上的,所以t1进来后,就锁了。
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
}
}
6)实现效果:
这下,就没有重复和负数票的情况啦。