线程间通讯1:
线程之间有时候需要交替执行,一个线程执行了一次然后唤醒另一个线程继续执行。例子:
需求:编写三个类,Ticket,Window,TicketCenter分别代表票信息、售票窗口、售票 中心。 若干个售票窗口进行出售,售票中心分配一定数量的票给窗口。
步骤1:
分配票的功能叫distributing()由票务中心执行(ticketcenter),卖票的功能叫做sell()由窗口执行(windows)。
class Ticket
{
String name;
intnum;
booleanflag = false;
Ticket(intnum)
{
this.num = num;
}
public synchronized void distributing (String name)
{
while(flag)
{
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name= name+"-//"+num--;
System.out.println(Thread.currentThread().getName()+"...distribute the ticket to"+this.name);
flag = true;
this.notifyAll();
}
步骤2 窗口的的代码是:
class ticketcenter implements Runnable {
Ticket cp;
windows(Ticket cp)
{
this.cp = cp;
}
public void run() {
while( this.cp.num>0 )
{
cp.distributing("。。。");
}
}
}
步骤3 票务中心的代码是:
class windows implements Runnable {
Ticket cp;
windows(Ticket cp)
{
this.cp = cp;
}
publicvoid run() {
while( this.cp.num>1 )
{
cp.sell("windows");
}
}
}
步骤4 最后main 方法里实现线程:
Ticket cp = new Ticket(1000);
windows wd = new windows(cp);
ticketcenter tc = new ticketcenter(cp);
Thread t1 = new Thread(tc,"ticketcenter");
Thread t2 = new Thread(wd,"window one ");
Thread t3 = new Thread(wd,"windosw two");
Thread t4 = new Thread(wd,"windosw three");
t1.start();
t2.start();
t3.start();
t4.start();
输出结果是:
每当一个售票点卖出去了一个票,票务中心就可以传送一个票据的编号到售票点,然后这个编号的票就被打印出来送到顾客手中,之后这个票的编号就永远作废了,不会再被售出。当然也可以反过来让售票中心先给票到窗口然后由窗口出售这些票。
注意点是:
当线程进入锁的状态后如果要利用notifyall 去唤醒所有的线程,不然会出现一个己方的线程在获得执行权之后试图去进入锁,但是不能通过锁去执行,而对面的线程有符合锁的执行条件但是没有抢到执行权,最后变成了所有的线程都在无限期等待的死亡状态。
线程间通讯2:
用notifyall去唤醒线程是可以的,但是会去唤醒所有的线程包括自己的线程,只不过自己这一边的线程因为不能通过锁所以不会进入运行状态,但是我们唤醒并且让己方线程去通过锁的过程就是一个浪费资源的行为,为了解决这个浪费问题,JDK1.5之后有了一个新的工具就是接口Lock。用下边的例子来显示如何使用这个Lock 接口
class TicketL
{
private String name;
intnum =1000;
booleanflag = false;
Lock lk = new ReentrantLock();
Condition tcl = lk.newCondition();
Condition wl = lk.newCondition();
public void sendTicket(String name) throws InterruptedException
{
lk.lock();
try
{
while(flag)
{tcl.await();}
this.name= name+"--"+num--;//输入名字的时候也给加上编号
System.out.println(Thread.currentThread().getName()+"..send Ticket :.."+this.name);
flag = true;
wl.signal();
}
finally
{
lk.unlock();
}
}
public void sell() throws InterruptedException
{
lk.lock();
try
{
while(!flag)
{wl.await();}
System.out.println(Thread.currentThread().getName()+"..sell ticket "+this.name);
flag=false;
tcl.signal();
}
finally
{
lk.unlock();
}
}
1.先初始化lock 接口实现类ReentrantLock命名为lk。然后初始化condition接口,用lock 的实例lk去调用newCondition(),然后弄两个condition的实例叫做tcl(ticketcenter lock)和 wl(windows lock),分别代表票务中心锁和窗口锁。
Lock lk = new ReentrantLock();
Conditiontcl = lk.newCondition();
Conditionwl = lk.newCondition();
<em><strong>//2.编写分票(sendticket)由票务中心类(TicketCenter)执行该方法</strong></em>
public void sendTicket(String name) throws InterruptedException
{ lk.lock();
try
{ while(flag)
{tcl.await();}
this.name= name+"--"+num--;//输入名字的时候也给加上编号
System.out.println(Thread.currentThread().getName()+"..send Ticket :.."+this.name);
flag = true;
wl.signal();
}
finally
{
lk.unlock();
}
}
简写模式如下,用lk上锁,然后释放锁。
lk.lock() try{ 判定锁,票务中心线程关闭,票务中心功能代码块,开锁,窗口线程开始}finally {最后开锁}
3编写卖票(sell)方法,由窗口类(windows)执行
public void sell() throws InterruptedException
{
lk.lock();
try
{
while(!flag)
{wl.await();}
System.out.println(Thread.currentThread().getName()+"..sell ticket "+this.name);
flag=false;
tcl.signal();
}
finally
{
lk.unlock();
}
}
lk.lock() try{ 判定锁,窗口线程关闭,窗口功能代码块,开锁,窗口线程开始}finally{最后开锁}
使用该方法我们窗口功能使用完毕后不用去唤醒包含窗口中心的线程在内的所有的线程,我们只需要通过使用tcl.signal()的方法唤醒所有票务中心的线程就可以了。这样节省了时间提高了效率。
剩下的部分和使用synchronized的方法是一样的这里就不赘述了。运行效果如图所示:
几种常用的线程方法:
如何让一个线程持续输出完毕后别的线程再继续输出:join方法
class Demo implements Runnable
{
publicvoid run() {
for(intx = 0 ; x<70;x++)
{
System.out.println(Thread.currentThread().toString()+" .... "+x); }
}
publicclass JoinDemo {
public static void main(String[] args) throws InterruptedException{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join();
t2.start();
for(intx = 0; x<80; x++){
System.out.println("main...."+x);
}
System.out.println("over");
}
}
}
t1.join可以让t1这个线程先运行完毕后再让t2 和main 线程互相竞争执行权,让他们规则或者不规则的交替执行。
Thread.currentThread().toString()方法可以显示线程编号-线程优先级,和运行线程的代码块。输出结果是:
如果是
t1.start();
t2.start();
t1.join();
这样排列的话,线程会优先交替执行t1,t2,而当t1执行完毕后,t2 和 main 线程才会交替执行。
如上图所示,线程0 到了69 之后main才开始进行第一次执行
//如果在
for(intx = 0 ; x<70;x++)
{
System.out.println(Thread.currentThread().toString()+" .... "+x);
Thread.yeild();
}
内放Thread.yeild(); 会让线程运行完之后释放执行权好让其他的线程有机会运行,运行结果就是这个:
没有一个线程可以连续执行。main ,1,0 线程可以交替执行。
关闭线程的方法:setDaemon()方法
t1.setDaemon(true)方法也叫守护线程会开启一个后台线程,在t1.start()之前放置,如果其他所有的前台线程都使用完毕了就剩下后台线程,系统就会结束守护线程以防止线程结束后不能关闭的情况。