Thread.docx
《Thread.docx》由会员分享,可在线阅读,更多相关《Thread.docx(8页珍藏版)》请在冰点文库上搜索。
Thread
线程
(一)
在java.lang.Thread包中
Thread类由jdk提供,用于封装对jvm中线程的控制
使用线程的一些原因是它们可以帮助:
∙利用多处理器系统
∙简化建模
∙执行异步或后台处理
一、创建一个线程知识点:
jvm会默认创建一个线程叫主线程(main),用来运行主方法,程序员可以自定义线程
线程是一个抽象的概念,我们将线程理解为就是一个任务
一个进程中可以有多个线程
1、进程中的多任务
2、线程的基本创建方式:
写一个类继承Tread,重写run方法,这个方法要指定线程的运行体
publicstatic
publicvoidrun(){
…..
}
Threadt=newThread();
t.start();
Thread.sleep(longl):
静态方法,不需要new对象,直接类名点调用
作用:
让当前线程挂起,挂起l毫秒(让运行该线程的线程挂起)
如:
Thread.sleep(1000);//是由主线程运行的,所以主线程运行到这,休息一秒,让创建的子方法运行
在一个线程对新线程的Thread对象调用start()方法之前,这个新线程并没有真正开始执行。
Thread对象在其线程真正启动之前就已经存在了,而且其线程退出之后仍然存在。
这可以让您控制
或获取关于已创建的线程的信息,即使线程还没有启动或已经完成了。
线程会以以下三种方式之一结束:
∙线程到达其run()方法的末尾。
∙线程抛出一个未捕获到的Exception或Error。
∙另一个线程调用一个弃用的stop()方法。
弃用是指这些方法仍然存在,但是您不应该在新代码中使用它们,并且应该尽量从现有代码中除去它们。
当Java程序中的所有线程都完成时,程序就退出了。
ThreadAPI包含了等待另一个线程完成的方法:
join()方法。
当调用Thread.join()时,调用线程将阻塞,直到目标线程完成为止。
Thread.join()通常由使用线程的程序使用,以将大问题划分成许多小问题,每个小问题分配一个线程
二、线程状态图:
重点(线程启动之后的状态)
1、线程有7个状态:
1、New:
新建
2、Ready:
就绪每个新建的线程只能start()一次
3、Running:
运行一个就绪的线程只能运行,他的运行由线程调度器来管理。
一个正在运行的线程除了NEW去不了其他的线程状态都可以去。
Yield可以让运行的线程回到就绪或者CPU事件片用完也可以让他回到就绪
4、Dead:
死亡Run(){}结束后本线程结束。
未捕获异常也会结束本线程。
线程一旦销往永远不可逆。
5、Blocked阻塞,阻塞的线程不能运行。
Sleep,join,等待用回输入(如scanner)都可以进入阻塞状态.阻塞的状态接触阻塞将会进入就绪。
当Sleep的事件到了后就会接触阻塞,JOIN也是时间到了就会解除阻塞,等待用回输入等提交后解除阻塞。
6、对象锁池,同步的线程在对象锁池等待。
Q;怎样的线程进入对象锁池:
A;1.只要遇到同步语句快2.没有对象锁
7、等待池
2、线程的状态及其转换
就绪----cpu调度---->运行
运行----cpu调度---->就绪注意:
正在运行的线程只有一个,但等待的线程可以有多个
运行--------------->阻塞由于某种原因不能再运行,出现阻塞状态如:
等待用户输入(资源),或者调用sleep方法。
必须等(线程别挂起)到资源到来,或者时间片到了
阻塞---------------->就绪条件到来时,会变到就绪状态,(等待cpu空闲才能别调度)
注意:
图中标记依次为
①输入完毕;②wakeup③t1退出
⑴如等待输入(输入设备进行处理,而CUP不处理),则放入阻塞,直到输入完毕。
⑵线程休眠sleep()
⑶t1.join()指停止main(),然后在某段时间内将t1加入运行队列,直到t1退出,main()才结束。
特别注意:
①②③与⑴⑵⑶是一一对应的。
进程的休眠:
Threadsleep(1000);//括号中以毫秒为单位
当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。
Try{Thread.sleep(1000);}
Catch(Exceptione){e.printStackTrace(e);}
T1.join()表示运行线程放弃执行权,进入阻塞状态。
当t1结束时,main()可以重新进入运行状态。
T1.join实际上是把并发的线程编程并行运行。
线程的优先级:
1-10,越大优先级越高,优先级越高被OS选中的可能性就越大。
(不建议使用,因为不同操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。
注:
程序的跨平台性:
除了能够运行,还必须保证运行的结果。
一个使用yield()就马上交出执行权,回到可运行状态,等待OS的再次调用
3、守护线程
t.setDaemon(true);//设置守护线程
主线程结束后守护线程也就结束了
通过setDaemon方法,可以设置线程为守护线程。
JVM如果发现当前的线程全部是守护线程时,就退出
如:
出题程序中计时线程应该为守护线程
4、Runnable接口
为什么要使用Runnable接口?
使用这种实现接口的机制,来解决Java语言不支持的多重继承的问题。
Runnable接口提供了run()方法的原型,因此,在希望定制的线程类中,只要实现此接口,也就是只要用特定的行为实现Runnable接口中的run()方法,即可实现新线程类的运行行为。
三、我们创建线程有两种方法:
1、写一个MyThreadextendsThread;Threadt=newMyThread();
线程的运行体是:
MyThread的Run()方法
eg.继承Thread
publicclassMyThread_1extendsThread
{
publicvoidrun()
{
//somecode
}
}
当使用继承创建线程,这样启动线程:
newMyThread_1().start()
2、Threadt=newThread(r);其中r是实现了Runnable接口的类的对象
classSomeClassimplementsRunnable{publicvoidrun(){...}}
Threadt=newThread(r);
使用Runnable接口,保证这个对象体一定有run方法
publicvoidrun(){
。
。
。
。
}
}
eg.实现Runnable接口
publicclassMyThread_2implementsRunnable
{
publicvoidrun()
{
//somecode
}
}
当使用实现接口创建线程,这样启动线程:
newThread(newMyThread_2()).start()
注意,其实是创建一个线程实例,并以实现了Runnable接口的类为参数传入这个实例,当执行这个线程的时候,MyThread_2中run里面的代码将被执行。
6、两个以上的线程去访问同一个(可变)对象,就有可能出现线程安全问题
我们使用加锁机制,保证一个线程去修改对象时,另一个线程不可以访问对象
同步方法:
synchronized难,但必须要掌握:
语法格式:
Synchronizedvoidmethod(){}
Synchronized在方法的前面
Voidmethod(){
Synchronized(Object){他后面不一定放THIS任何对象都可以
。
。
。
。
语句块
}
}
让这个对象看住这个语句块,某一时刻只允许一个线程来访问一个语句块
使用方法:
object只有一把钥匙,只有拿到这边钥匙,才能执行被{}框起来的语句块,其他的线程来了,就只能等待这把钥匙。
刚才拿到钥匙的线程处理时就必须归还钥匙
注意:
1、{}要括哪些代码
2、谁来看语句块
publicclassABC{
privateListlist...
publicvoidaddToList(){
....
}
syncronized(newDate()){//这样每次加载一次程序,就会创建一个新的对象,不可以使用这个对象来看,我们使用一个固定的(不可变的)对象,可以使用list即this本对象
list.add(....){
}
}
问:
这个类是否是线程安全的?
也就是问如果有两个以上的线程去使用该类的同一个对象,是否会有线程安全问题
如:
也不可以是一个调用addList(),一个调用remove()方法
怎样将这个类变成线程安全的?
不安全的因素在:
一个调用addList(),一个调用remove()方法,我们采取加锁的方法,要考虑加锁的范围,这样可以提高效率,在这里我们只有将list.add(n)和list.move(n);
各自放入到synchronized中,这个对象就是list本身,(如monitor),当然如果我们自己定义了一个Object对象来看门的话,会出现add和remove可以同时进,(list来看add,Object看remove),所以我们将这两个对方使用同一个对象来看,只有一个monitor
注意:
1、publicvoidf(){
syncronized(this){//使用this看门并且所有的方法全部括起来
.....
}
}
等同于:
publicsynchronizedvoidf(){
.....
}
2、PushThreadt1=newPushThread();不算线程
Threadt2=newThread();是线程
7、了解线程的一些基本方法:
使用getName来线程标识。
t.getName
getId,是唯一的,线程编号不能改,所以没有setId
t.setPriority(newPriority);//设置优先级,数目越大的,线程的优先级越高
一般情况不要去改优先级,默认主线程的优先级为5
因为由于垃圾回收机制比较高
我们一般创建线程的优先级最后不要超过5