多线程的错误一般都是偶尔发生的不可重现的错误!
为什么要用同步?
多线程是时间片轮转的,当A线程试图对某个资源(对象,变量
文件)作出合理的操作时,时间片用完了,以后的B线程,C线程等
或许对这个资源做出了操作并且导致了这个资源地再次操作是不
合理的,当A线程再次到来时,它并不知道这个操作已经被标识为
不合理,所以肯定要非法操作了!
同步保护的是临界区:从同时执行的多个线程中访问同一个对象或者说资源
的代码叫做临界区;
同步块方法:
synchronized(obj)
{
//临界区
}
每个obj都有一个监视器,当A线程进入同步块的时候,obj被加锁,
只有当A线程走出临界区后才被解锁,B线程等才可以访问临界区,
否则将都会等待;
就好像有个人要进一个房间办点事情,他进去的时候把门锁了,
只有当他完成工作出去的时候才开门;在这段时间即使他在里面
睡觉,其他人也不能进来,只能等待;
用同步方法:
public synchronized void sell()
{
临界区
}
当线程进入同步方法的时候,会察看this对象的监视器有没有加锁,
没有的话就会加锁,直到方法返回的时候解锁;被加锁的话就等
待;
如果你的多个线程用的是同一个同步块或者同步方法,那临界区肯定是同步的;
如果你的多个线程使用了不同的同步块或者同步方法来达到临界区的
同步,那你必须保证这些同步块或同步方法都使用了同一个对象的监视器做
加锁用,不然的话,还是不同步;
同步静态方法使用的是这个类所对应的Class对象的监视器;
注意这个陷阱:
main()
{
new Thread(runnable).start();
runnable.cache=true;//在Runnable的实现类中初始的runnable.cache=false;
new Thread(runnable).start();
}
也许我们要达到的效果是:第一个线程中的cache是false,而第二个线程中的cache是true;
但是我们通常达到的效果却是:两个线程中的cache都是true;
为什么呢?因为当主线程的一个连续的时间片足以执行完这三句话时,
new Thread(runnable).start()并没有启动第一个线程,因为此时是主线程在运行;
当主线程结束时,等待的第一个线程启动,这时候它看到的cache已经被设置为true,而不是false;
解决的办法是当new Thread().start()后让主线程睡眠一会以让第一个线程启动
线程的死锁
在线程1中:
synchronized(objA)
{
synchronized(objB);
}
在线程2中:
synchronized(objB)
{
synchronized(objA);
}
象上面的这种情况,就发生了死锁;死锁的原始理解是说两个线程互相等待对方
锁定的资源,而在未得到对方锁定的资源的时候自己不会释放自己锁定
的资源,就发生了死锁;
但是现在看来,也不是为了什么资源,而是为了一个用来同步临界区资源的
对象的监视器;
Object对象就有wait(),notify()等专门为线程设计的方法,说明一切对象
都与线程息息相关;
必须明确的是:等待和睡眠是两回事;而且一个是由同步所用的对象来安排线程的动作和状态;
另一个是由线程自己来做的;