java学习笔记5

时间:2023-02-24 12:38:47

(1)   死锁

有多个进程, 且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操

作,那么就称它们被死锁了。要避免死锁, 应该确保在获取多个锁时,在所有的线程中都以相同的顺序获取锁。在下面的例子中, 程序创建了两个类 A 和 B,它们分别具有方法 funA()和 funB(),在调用对方的方法前,funA()和 funB()都睡眠一会儿。主类 DeadLockDemo 创建 A 和B 实例,然后,产生第二个线程以构成死锁条件。funA()和 funB()使用 sleep()方法来强制死锁条件出现。

public class A {
synchronizedvoid funA( B b)
{
String name =Thread.currentThread().getName();
System.out.println(name+"进入A.foo");
try {
Thread.sleep(1000);
} catch (Exception e)
{
System.out.println( e.getMessage() );
}
System.out.println(name+"调用B类中的last方法");
b.last();
}
synchronizedvoid last()
{
System.out.println("A 类中的last()方法");
}
}
public class B {
synchronizedvoid funB(A a)
{
String name = Thread.currentThread().getName();
System.out.println(name+"进入B类中的");
try {
Thread.sleep(1000);
} catch (Exception e)
{
System.out.println( e.getMessage() );
}
System.out.println(name+"调用A类中的last()方法");
a.last();
}
synchronizedvoid last()
{
System.out.println("B类中的last()方法");
}
}
public class DeadLockDemo implementsRunnable {
Aa = new A();
B b = new B();
DeadLockDemo()
{
Thread.currentThread().setName("main-->>thread");
new Thread(this).start();
a.funA(b);
System.out.println("main线程运行完毕!");
}
publicvoid run()
{
Thread.currentThread().setName("test-->>Thread");
b.funB(a);
System.out.println("其他线程运行完毕");
}
public static void main(String []args)
{
new DeadLockDemo();
}
}

从运行结果可以发现,Test-->> Thread 进入了 b 的监视器,然后又在等待 a 的监 视器。同时 Main-->> Thread 进入了 a 的监视器并等待 b 的监视器。这个程序永远不

会完成。

(2)   线程间通讯

代码示例:

public class Producer implements Runnable
{
Pq = null;
public Producer(P q)
{
this.q = q;
}
publicvoid run()
{
int i = 0;
while( true )
{
if( i == 0)
{
q.set("张三","男");
}
else
{
q. set("李四","女");
}
i=( i + 1 ) % 2;
}
}
}
public class Consumer implements Runnable
{
P q =null;
public Consumer( P q)
{
this.q = q;

}
public void run()
{
while( true )
{
q.get();
}
}
}
public class P {
Stringname = "李四";
Stringsex = "女";
publicsynchronized void set(String name, String sex)
{
this.name= name;
this.sex=sex;
}
publicsynchronized void get( )
{
System.out.println(this.name+ "------> "+ this.sex );

}

}
public class ThreadCommunation {
publicstatic void main(String[] args)
{
P q = new P();
newThread(new Producer(q) ).start();
newThread (new Consumer(q) ).start();
}

}


运行发现程序的输出结果是正确的,但是这里问题产生了,从程序的执行结果来看, Consumer 线程对 Producer 线程放入的一次数据连续读取了多次,并不符合实际的要求。实际要求的结果是,Producer 放一次数据,Consumer 就取一次;反之, Producer 也必须等到 Consumer 取完后才能放入新的数据, 而这一问题的解决就需要使用下面所要讲到的线程间的通信。

Java是如何实现通信的

Java 是通过 Object 类的 wait、 notify、 notifyAll

这几个方法来实现线程间的通信的,又因为所有的类都是从 Object 继承的,所以任何类都可以直接使用这些方法。下面是这三个方法的简要说明:wait:告诉当前线程放弃监视器并进入睡眠状态,直到其它线程进入同一监视器并调用 notify 为止。

notify:唤醒同一对象监视器中调用 wait 的第一个线程。类似排队买票,一个人买完之后,后面的人可以继续买。

notifyAll:唤醒同一对象监视器中调用 wait 的所有线程,具有最高优先级的线程首先被唤醒并执行。

通过对calss p 的改造完全满足所想

public class Producer implements Runnable
{
Pq = null;
public Producer(P q)
{
this.q = q;
}
publicvoid run()
{
int i = 0;
while( true )
{
if( i == 0)
{
q.set("张三","男");
}
else
{
q. set("李四","女");
}
i=( i + 1 ) % 2;
}
}
}
public class Consumer implements Runnable
{
Pq =null;
public Consumer( P q)
{
this.q = q;

}
public void run()
{
while( true )
{
q.get();
}
}
}
public class P {
Stringname = "李四";
Stringsex = "女";
booleanbFull = false;
publicsynchronized void set(String name, String sex)
{
if(bFull)
{
try{
wait();
}catch(InterruptedException e)
{}
}
this.name= name;
try{
Thread.sleep(10);
} catch (Exception e) {
System.out.println(e.getMessage() );
}
this.sex= sex;
bFull= true;
notify();
}
publicsynchronized void get( )
{
if(!bFull )
{
try{
wait();
}catch (InterruptedException e)
{}
}
System.out.println(this.name+ "------> "+ this.sex );
bFull= false;
notify();
}

}
public class ThreadCommunation {
publicstatic void main(String[] args)
{
P q = new P();
newThread(new Producer(q) ).start();
newThread (new Consumer(q) ).start();
}

}


wait、notify、notifyAll 这三个方法只能在 synchronized 方法中调用,即无论线程调用一个对象的 wait 还是 notify 方法, 该线程必须先得到该对象的锁标记,这样, notify

只能唤醒同一对象监视器中调用wait 的线程,使用多个对象监视器,就可以分别有多个 wait、notify 的情况,同组里的 wait 只能被同组的 notify 唤醒。

线程的等待和唤醒过程如下图所示:

java学习笔记5