在我们写java小游戏代码中,很多时候我们都会用到线程,如果程序代码只有主函数一个单线程,就无法实现比如说一些方法同步问题,这时候我们需要采用多个线程去控制程序,实现相应的功能。
线程类Thread是Object类,在java.lang包当中,在我写“坚持100秒”这个小游戏的时候,我就用了一个简单的单独的线程来保持创建的小球不断的运行,这个代码相对较简单,我就不演示代码了。但有一种线程同步问题比较复杂,就两个线程操作同一个对象,如果我们不加以限制,得到的结果就往往不是我们想要得到的。举个例子说吧:在银行,用户有两张存折控制同一个账户,当两个存折在不同的地方同时去取钱的时候,就相当于两个线程同时操作一个对象,如果处理不好,银行可就亏大了。这种情况我们就得用synchronized(同步锁)来控制这两个线程对对象的操作,注意:synchronized要锁住两个线程共同操作的对象或者属性。这相当于这两个线程中的其中一个线程在操作这个共同的对象的时候,这时候synchronized就会锁住它控制的代码段,另外一个线程就无法操作由synchronized控制的代码段了。
一般在用同步锁控制线程的时候,我们常常会用到wait/notify机制来缓解cpu的运行压力,wait与sleep是存在本质的差别的,在执行wait方法的时候,是会释放cpu的,而sleep方法则不会。就拿典型的生产消费模型来说吧,代码如下所示:在这个例子中,生产者和消费者共同操作list,只有当生产者生产好了手机产品放入list后,消费者才能从list中取到相应的产品。这时二者用wait和notify进行通信,共同操作list对象。
public class ProductThread extends Thread{
//定义一个集合,用来存储生产商生产出来的手机对象
public static ArrayList<MiPhone> list = new ArrayList<MiPhone>();
private int count = 0;
@Override
public void run() {
super.run();
synchronized(list){
//不停执行,生产手机
while(true){
if(list.size()>10){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MiPhone mp = new MiPhone();
mp.setName("小米"+count);
count++;
//把生产出来的手机添加到仓库list中
list.add(mp);
System.out.println("生产商,生产了一个小米手机,手机型号是:"+mp.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//提醒消费者,手机已经生产完成,可以进行销售
list.notify();
}
}
}
}
public class CustomerThread extends Thread {
@Override
public void run() {
super.run();
synchronized (ProductThread.list) {
while (true) {
// 判断仓库是否还有手机
if (ProductThread.list.size() == 0) {
try {
ProductThread.list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 从队列中移出一个手机
MiPhone mp = ProductThread.list.remove(0);
System.out.println("销售人员卖出了一个手机,手机型号为:" + mp.getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 提醒生产者,手机买完了,你必须继续生产
ProductThread.list.notify();
}
}
}
}
1)wait和notify必须在同步锁之内使用
2)同步锁锁定对象和wait、notify对象必须同一个
3)当对象wait挂起状态时候是会释放同步锁的