黑马程序员——JAVA基础之多线程的线程间通讯等

时间:2023-03-08 17:30:34
黑马程序员——JAVA基础之多线程的线程间通讯等

------- android培训java培训、期待与您交流!
----------

线程间通讯:

其实就是多个线程在操作同一个资源,但是动作不同。

wait();

在其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。

当前线程必须拥有此对象监视器。

notify();

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。

选择是任意性的,并在对实现做出决定时发生。

线程通过调用其中一个 wait 方法,在对象的监视器上等待。

notifyAll(); 

唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

思考1:wait ( ),notify ( ),notifyAll ( ),用来操作线程为什么定义在了Object 类中?

1,这些方法存在与同步中。

     2,使用这些方法时必须要标识所属的同步的锁。

     3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。 

 也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

思考2:wait ( ) ,sleep ( ) 有什么区别?

wait ( ) : 释放cpu执行权,释放锁。

     sleep ( ) : 释放cpu执行权,不释放锁。

/**
*
* 多线程通讯示例:
* 多线程通讯就是多个线程共同操作同一个资源,但是动作不同
* 例:开启两个线程,交替输入输出两个人的名字和年龄
*
*/
public class ThreadTest
{
public static void main(String[] args)
{
Person p = new Person();
Input in = new Input(p);
Output out = new Output(p); Thread t1 = new Thread(in);
Thread t2 = new Thread(out); t1.start();
t2.start();
}
} //声明一个Person类,有两个属性,姓名和性别
class Person
{
String name;
String sex;
} //声明一个输入线程继承Runnable。传入名字和性别
class Input implements Runnable
{
private Person p; Input (Person p)
{
this.p = p;
} public void run()
{
int x = 0; while(true)
{
synchronized (p)
{
if (x==0)
{
p.name = "lilei";
p.sex = "boy";
}
else
{
p.name = "hanmeimei";
p.sex = "girl";
}
x = (1+x)%2;
}
}
}
} //声明一个输出线程的类继承Runnable,输出姓名和性别
class Output implements Runnable
{
private Person p; Output(Person p)
{
this.p = p;
} public void run()
{
while(true)
{
synchronized (p)
{
System.out.println("name: "+p.name+"-------sex: "+p.sex);
}
}
}
}

等待唤醒机制:

改进一下这个程序,让输入输出交替进行

/**
*
* 多线程通讯示例:
* 多线程通讯就是多个线程共同操作同一个资源,但是动作不同
* 例:开启两个线程,交替输入输出两个人的名字和年龄
*
*/
public class ThreadTest
{
public static void main(String[] args)
{
Person p = new Person();
Input in = new Input(p);
Output out = new Output(p); Thread t1 = new Thread(in);
Thread t2 = new Thread(out); t1.start();
t2.start();
}
} //声明一个Person类,有两个属性,姓名和性别
class Person
{
String name;
String sex;
boolean flat;
} //声明一个输入线程继承Runnable。传入名字和性别
class Input implements Runnable
{
private Person p; Input (Person p)
{
this.p = p;
} public void run()
{
int x = 0; while(true)
{
synchronized (p)
{
if (p.flat)
try{p.wait();}catch(Exception e){}
if (x==0)
{
p.name = "lilei";
p.sex = "boy";
}
else
{
p.name = "hanmeimei";
p.sex = "girl";
}
x = (1+x)%2;
p.flat = true;
p.notify();
}
}
}
} //声明一个输出线程的类继承Runnable,输出姓名和性别
class Output implements Runnable
{
private Person p; Output(Person p)
{
this.p = p;
} public void run()
{
while(true)
{
synchronized (p)
{
if (!p.flat)
try{p.wait();}catch(Exception e){}
System.out.println("name: "+p.name+"-------sex: "+p.sex);
p.flat = false;
p.notify();
}
}
}
}

发现有些数据不能被外界直接调用,具有安全隐患,所以改进如下:

/**
*
* 多线程通讯示例:
* 多线程通讯就是多个线程共同操作同一个资源,但是动作不同
* 例:开启两个线程,交替输入输出两个人的名字和年龄
*
*/
public class ThreadTest
{
public static void main(String[] args)
{
Person p = new Person();
Input in = new Input(p);
Output out = new Output(p); Thread t1 = new Thread(in);
Thread t2 = new Thread(out); t1.start();
t2.start();
}
} //声明一个Person类,有两个属性,姓名和性别
class Person
{
//私有数据,更安全
private String name;
private String sex;
private boolean flat; public synchronized void set(String name,String sex)
{
if (flat)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flat = true;
this.notify();
} public synchronized void out()
{
if (!flat)
try{this.wait();}catch(Exception e){}
System.out.println("name: "+name+"-------sex: "+sex);
flat = false;
this.notify();
}
} //声明一个输入线程继承Runnable。传入名字和性别
class Input implements Runnable
{
private Person p; Input (Person p)
{
this.p = p;
} public void run()
{
int x = 0; while(true)
{
if (x==0)
p.set("lilei","boy");
else
p.set("hanmeimei","girl");
x = (x+1)%2;
}
}
} //声明一个输出线程的类继承Runnable,输出姓名和性别
class Output implements Runnable
{
private Person p; Output(Person p)
{
this.p = p;
} public void run()
{
while(true)
{
p.out();
}
}
}

为什么定义notifyAll:

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

/**
*
* 当出现两个以上多线程进行多线程通讯的时候,需要用的notifyAll();
* 例子:
* 生产者消费者
*
*/
public class ProCusDemo
{
public static void main(String[] args)
{
Resource r = new Resource(); Pro p = new Pro(r);
Cus c = new Cus(r); Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c); t1.start();
t2.start();
t3.start();
t4.start();
}
} //声明一个资源类,赋予基本属性
class Resource
{
private String name;
private int count = 1;
private boolean flat; public synchronized void set(String name)
{
while (flat)//用while让被唤醒的线程再一次判断标记
try{wait();}catch(Exception e){}
this.name = name+"-----"+count++; System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
flat = true;
notifyAll();
} public synchronized void out()
{
while (!flat)//用while让被唤醒的线程再一次判断标记
try{wait();}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"---消费者---"+this.name);
flat = false;
notifyAll();
}
} class Pro implements Runnable
{
private Resource r; Pro(Resource r)
{
this.r = r;
} public void run()
{
while (true)
{
r.set("商品");
}
}
} class Cus implements Runnable
{
private Resource r; Cus(Resource r)
{
this.r = r;
} public void run()
{
while (true)
{
r.out();
}
}
}

JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

将Object中的wait,notify notifyAll,替换了Condition对象。

该对象可以Lock锁进行获取。

该示例中,实现了本方只唤醒对方操作。

 

Lock:替代了Synchronized 

    lock 

    unlock

    newCondition()

 

Condition:替代了Object wait notify notifyAll

    await();

    signal();

    signalAll();

<span style="font-size:14px;">import java.util.concurrent.locks.*;

class ProducerConsumerDemo2
{
public static void main(String[] args)
{
Resource r = new Resource(); Producer pro = new Producer(r);
Consumer con = new Consumer(r); Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con); t1.start();
t2.start();
t3.start();
t4.start(); }
} class Resource
{
private String name;
private int count = 1;
private boolean flag = false; private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();//t1,t2
this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signal();
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
} public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
} class Producer implements Runnable
{
private Resource res; Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch (InterruptedException e)
{
} }
}
} class Consumer implements Runnable
{
private Resource res; Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
</span>

如何停止线程:

1.  定义循环结束标记,因为线程运行代码一般都是循环,只要控制了循环即可。

     2.  注:stop方法已经过时不再使用。

stop方法已经过时,如何停止线程?



       只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

       特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。使用interrupt(中断)方法。

        该方法是结束线程的冻结状态,使线程回到运行状态中来。

<span style="font-size:14px;">class StopThread implements Runnable
{
private boolean flag =true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread.getName()+".....Exception");
flag = false;
}
System.out.println(Thread.currentThread.getName()+".......run");
}
}
public void changeFlag()
{
flag = false;
}
} class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread(); Thread t1 = new Thread(st);
Thread t2 = new Thread(st); t1.start();
t2.start(); int num = 0; while(true)
{
if(num++ == 60)
{
//st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
</span>

守护线程:

前台线程执行完之后后台线程自动执行完,主线程是前台线程。

<span style="font-size:14px;">class StopThread implements Runnable
{
private boolean flag =true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread.getName()+".....Exception");
flag = false;
}
System.out.println(Thread.currentThread.getName()+".......run");
}
}
public void changeFlag()
{
flag = false;
}
} class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread(); Thread t1 = new Thread(st);
Thread t2 = new Thread(st); t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start(); int num = 0; while(true)
{
if(num++ == 60)
{
//st.changeFlag();
//t1.interrupt();
//t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
</span>

join用法:

当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。


 

join可以用来临时加入线程执行。

<span style="font-size:14px;">class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().getName()+"...."+x);
}
}
} class JoinDemo
{
public static void main(String[] args) throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join();//此时t1线程获得执行权,主线程冻结。
t2.start(); for(int x=0; x<80; x++)
{
System.out.println("main....."+x);
}
System.out.println("over");
}
}
</span>

yield() :

可以稍微减缓程序运行,产生近似于交替运行的效果。

class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().toString()+"....."+x);//toString()显示优先级,优先级1~10,默认5
Thread.yield();//让程序稍微暂停一下,释放执行权,产生交替运行的效果
}
}
} class JoinDemo
{
public static void main(String[] args) throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//t1.setPriority(Thread.MAX_PRIORITY); 设置最大优先级
t2.start(); for(int x=0; x<80; x++)
{
System.out.println("main....."+x);
}
System.out.println("over");
}
}

------- android培训java培训、期待与您交流!
----------