一:什么是线程
可以把一个正在运行的软件看做一个进程,就像一个大的管道,这个管道不运送什么东西,但里面有很多个小的管道,每个小管负责的东西不同,而这些小管道就可以看做是一个个线程。
如果运行的是一个单线程的程序的话,而这个线程需要连续运行几个功能时如果正在运行的那个功能因碰到一个等待或者睡眠的指令的话,他就会停在那里不做任何事,此是这个CPU就空闲在那里,同时还会等待直到程序重新继续运行。
如果使用多线程技术,那么可以把这几个功能同时(并不是绝对意义上的时)运行,当其中一个功能遇到睡眠指令的时候,其他没有睡眠的继续运行,这个可以花更短的时间,让CPU更充分的被利用来完成需要的事情~线程通常共享一个代码区,但有各自独立的数据存储区 。
二:线程如何实现
1. 继承:
1.1继承Thread类覆盖run方法
1.2Thread类中的方法
getName():用来获取线程的名字。但这个方法只是打印线程子类对象的名字。
currentThread():这个方法返回正在运行的对象结合getName()方法就可以返回当前正在运行的线程。
1.3.线程的开启:
线程在被创建之后并不能被使用,还需要开启。所以要使用start方法,而start方法被使用之后做了两件事一个是开启线程,另一个就是调用run方法。
多线程之间是互相不干扰的,一个程序出现异常另一个并不会发生异常。
2. 定义一个类实现Runnable接口
2.1.覆盖接口中的run方法。就是将线程要运行的代码储存到run方法中
2.2创建该接口的子类对象。
2.3通过Thread类进行线程的创建,并将Runnable接口的子类对象作为Thread类构造函数的实参进行传递。
2.4那么为什么要将Runnable的子类对象传递呢:让线程对象创建时,就是要明确要运行哪个run方法,而这个run方法是要被调用的,所以将run方法所属的对象传递给Thread类的构造方法。
2.5调用Thread类中的start方法开启线程。
2.6那么怎么理解将对象当作参数传递过去呢:
Runnable接口的由来其实就是将线程的任务进行对象的封装。将线程任务封装成对象后,通过Runnable接口可以降低和Thread对象的耦合性。
注意
如果是继承Thread类,覆盖run方法这种情况。Thread的子类即封装了线程的任务而且自身还是一个线程对象这就是任务和对象耦合性过高。
代码:
1、继承Thread
class Demo extends Thread
{
public void run()
{
//线程任务
}
}
Demo d=new Demo();//这里一个带有具备任务的线程对象。
a.start();
2.如果是Runnable接口
class Demo implements Runnable
{
public void run()
{
//线程的任务。
}
}
Demo d=new Demo();//这是一个具备任务的对象。
Thread t1=new Thread(d);//创建线程对象吧封装了线程任务的对象传入
三:多线程时如何运行的:
当有多个线程时每个线程都有自己的运行线路互不干扰,一个线路异常并不会影响到另一个
四:线程的几种运行状态:
线程的运行大概就是分为这5种方式其中最特殊的就是临时阻塞状态
五:线程的安全问题:
5.1. 安全问题产生的原因:
5.1.1多个线程在操作共享数据。
5.1.2操作共享数据的代码有多条。
5.2 如何解决:
只需要在别的线程在执行共享数据的时候其他线程不参与其中就行了。那么这时我们就能利用到同步:
代码格式:
synchronized(对象) {
需要同步的语句。
}
public void run() {
while(true) {
synchronized(obj) { //这个同步对象相当于一把锁。当0线程在处理时1线程和其他线程是进不去的只有他执行完才行。
if(num>0) {
try{
Thread.sleep(10);
} catch(InterruptedException e){}
sop(Thread.currentThread().getName()+"…sale…."num--);
}
}
六:同步的弊端:
6.1 死锁
6.1.1 死锁产生的条件:互斥条件,不可抢占条件,占有且申请条件,循环条件。
互斥条件:
即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独木桥就是一种独占资源,两方的人不能同时过桥.
不可抢占条件:
进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,
而只能由该资源的占有者进程自行释放。如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。
占有且申请条件:
进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,
此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。
循环等待条件:
存在一个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,......,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待。
注意:
上面的这“四个条件”在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。
6.1.2 最常见的死锁情况:同步嵌套。
嵌套就是同步中又有一个同步。
代码:
if(flag)
{
while(true){
synchronized(obj)//当1线程进来时拿到了obj这个锁而线程2则在执行同步函数,当一线程执行show方法时2线程在拿着this锁所以没法执行。
{
show();
}
}
else
while(true){
)
)
public void setFlag(){
flag=false;
}
public synchronized void show()
{
synchronized(obj)
{
if(num>0){
System.out.println(Thread.currentThread().getName()+"…..sale…."+num----);
}
public static void main(String[]args){
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t2.start();
}
}