多线程的深入学习:单生产单消费,单生产多消费,多生产多消费,守护线程,线程优先级,join和yield,线程内部匿名类

 

以下三种情况都是在需要生产出一个,马上就消费一个的情况下:

单生产单消费:

单生产单消费是个简单的多线程,只需保证多线程同步,并且锁为同一把锁即可。代码体现:

class Demo{

private Object obj=new Object(); //创建锁对象obj

private int count=0;

private boolean flag=false;       //创建标记

public void set(){

synchronized(obj){             //同步线程

while(flag)

try {

obj.wait();             //线程等待

} catch (InterruptedException e) {

}

count++;

flag=true;

System.out.println("生产的产品"+count);

obj.notify();                //唤醒线程

}

}

public void sell(){

synchronized (obj) {

while(!flag)

try {

obj.wait();

} catch (InterruptedException e) {}

flag=false;

System.out.println("消费的产品"+count);

obj.notify();

}

}

}

class Set implements Runnable{

private Demo d;

public Set(Demo d){

this.d=d;

}

public void run(){

while(true){

d.set();

}

}

}

class Sell implements Runnable{

private Demo d;

public Sell(Demo d){

this.d=d;

}

public void run(){

while(true){

d.sell();

}

}

}

public class TextThread01 {

public static void main(String[] args) {

Demo d=new Demo();

Set st=new Set(d);

Sell sl=new Sell(d);

Thread t1=new Thread(st);

Thread t2=new Thread(sl);

t1.start();

t2.start();

}

}

以上是整个单生产单消费的程序,重点:1 保证锁必须为同一个,2线程等待唤醒的判断逻辑没问题。单生产多消费并不复杂.

多生产多消费和单生产和多消费就比单生产单消费复杂点了,按照上面的代码的逻辑来处理多生产多消费的话,会出现多线程的安全问题。下面分析原因:

先说单生产多消费的线程运行:生产线程1运行唤醒语句后可能还有执行权,然后继续判断,这时的flag为true,生产线程1便进入等待。唤醒线程照样执行,只是没有意义。然后,这时有2个活的消费线程,其中一个启动,运行结束后同样可能有执行权,判断(这时flag为false),消费线程1就进入等待,交出锁,因为这里只有生产线程处于等待状态,所以就唤醒线程1.到这里,生产线程1和消费线程2都有执行资格,如果消费线程2拿到锁,进入判断,又进入等待,把锁交出,只有生产线程1可以拿到,生产线程1拿到后运行后进入等待,唤醒消费线程中的任意一个(这里假设唤醒消费线程1),消费线程拿到执行权,执行完等待。这是有2个线程在等待,如果消费线程1唤醒的是消费线程2,这里的falg是false,线程2就进入等待,这里没有唤醒任何线程,就出现了死锁。所以单生产多消费用这样的代码实现是会出现安全问题的。

处理方案:将程序中的唤醒语句obj.notify换成obj.notifyAll可以解决安全问题,推导参照上述推导即可。换成全部唤醒后,线程的运行的效率就会变得很低。要解决这里的效率问题,需要用到jdk1.5新推出的locke类和其子类condition接口。lock类相当于锁,condition相当于监视器。lock类相较于同步来说,优势在于可以在一个锁内创建2个监视器。

代码体现:

class Demo{

private Object obj=new Object();

private int count=0;

private boolean flag=false;

private Lock lock=new ReentrantLock();

private Condition cot=lock.newCondition();

private Condition col=lock.newCondition();

public void set(){

//synchronized(obj){

lock.lock();try{

while(flag)

try {

cot.await();

} catch (InterruptedException e) {

}

count++;

flag=true;

System.out.println("生产的产品"+count);

col.signal();

//}

}finally{

lock.unlock();

}

}

public void sell(){

lock.lock();

try{

//synchronized (obj) {

while(!flag)

try {

col.await();

} catch (InterruptedException e) {}

flag=false;

System.out.println("消费的产品"+count);

cot.signal();

//}

}finally{

lock.unlock();

}

}

}

class Set implements Runnable{

private Demo d;

public Set(Demo d){

this.d=d;

}

public void run(){

while(true){

d.set();

}

}

}

class Sell implements Runnable{

private Demo d;

public Sell(Demo d){

this.d=d;

}

public void run(){

while(true){

d.sell();

}

}

}

public class TextThread01 {

public static void main(String[] args) {

Demo d=new Demo();

Set st=new Set(d);

Sell sl=new Sell(d);

Thread t1=new Thread(st);

Thread t2=new Thread(sl);

Thread t3=new Thread(sl);

t1.start();

t2.start();

t3.start();

}

}

多消费多生产和单生产单消费的机制是一样的,只需再创建一个生产对象即可,代码体现如上。

守护线程:

将线程定位守护线程后,该线程的特性为:当该多线程中只剩下守护线程在运行时,守护线程就停止运行。代码体现:

class Daemon extends Thread{

private int count=0;

public void print(){

System.out.println(Thread.currentThread().getName()+"***********"+count);

count++;

}

public void run(){

for(int i=0;i<1000;i++){

print();

}

}

}

public class TextDaemon01 {

public static void main(String[] args) {

//TextDaemon01 td=new TextDaemon01();

Daemon d=new Daemon();

Daemon d1=new Daemon();

d1.start();

d.setDaemon(true);

//d.setDaemon(true);

d.start();

}

}

这里有个疑问:主函数能不能作为守护线程?

线程优先级:

getPriority是返回线程的优先级,setPriority为设置线程的优先级。线程优先级为0-10级,我们设置的时候为了可以得到明显的线程优先效果,一般设置为MAX_PRIORITY,MINPRIORITY,NORM_PRIORITY.

Join yield方法:

Join方法的意思:线程2是加入一个线程1,线程1将释放执行全,等线程2执行完毕后,线程1才开始继续运行。先看代码体现:

public class TextDaemon01 {

public static void main(String[] args) throws InterruptedException {

//TextDaemon01 td=new TextDaemon01();

Daemon d=new Daemon();

Daemon d1=new Daemon();

d.start(); //语句1

d.join();//语句2

d1.start();//语句3

for(int i=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+i);

}

}

}

语句123现在这个情况就是主函数线程运行到语句1时,线程1启动,语句2时主函数线程开始等待线程1的执行完毕,再和线程2抢夺执行权开始运行。

d.start(); //语句1

d1.start();//语句3

d.join();//语句2

当语句123是上面的情况时,线程12交替运行,等线程1运行完毕后主函数线程才开始运行。这里说明一点:主线程释放执行权不是指向的,活线程都可以抢夺执行权并执行。

Yield:

这个方法的意思:让线程暂时暂停,但是该线程可能获取到执行权。

线程在匿名内部类的的表现:

代码体现:

public class TextThread02 {

public static void main(String[] args) {

new Thread(){

public void run(){

while(true){

System.out.println("......");

}

}

}.start();;

}

}

就是将线程写入匿名内部类,功能没变化。

你可能感兴趣的:(多线程的深入学习:单生产单消费,单生产多消费,多生产多消费,守护线程,线程优先级,join和yield,线程内部匿名类)