多线程二
9、线程间通信
线程间通信其实就是多个线程在操作同一个资源但是操作的动作不同
9.1、示例:
有一堆煤,挖煤线程向其中送煤,运煤线程从其中运走煤,一进一出同时运行,但input与output线程执行的动作不一致(run方法不一样)
9.2、程序实例:
有一个个人信息库(个人信息为姓名、性别),input线程向其中传个人数据,output线程从其中取出数据,要达到的效果是:输入线程输入
一条个人信息(包括name、sex),输出线程就输出这条个人信息(包括name、sex),两个线程彼此交替执行
9.2.1、代码:
<pre name="code" class="java">class Res
{
String name;
String sex;
boolean flag=false; //flag值是控制输入、输出线程获取执行权的条件:“false”只进不出,“true”只出不进
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
synchronized(Input.class)//"Input.class"为类Input的字节码文件对象
{
if (r.flag)//当flag为true时,当前线程被冻结,flag为false时,程序向下执行
try
{
Input.class.wait();
}
catch (Exception e)
{
}
if(x==0)
{
r.name="马克";
r.sex="男";
}
else
{
r.name="莉莉";
r.sex="女";
}
x=(x+1)%2;//取模(取余):0传入,x=1;1传入,x=0
r.flag=true;
Input.class.notify();//唤醒在此对象锁上等待的单个线程
}
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r=r;
}
public void run()
{
while (true)
{
synchronized(Input.class)
{
if (!r.flag)//当!flag=trues时,即flag=false时,当前线程被冻结,flag=ture时,向下执行
try
{
Input.class.wait();//输入线程无法获得执行权 等待
}
catch (Exception e)
{
}
System.out.println(r.name+",...."+r.sex);
r.flag=false;
Input.class.notify();
}
}
}
}
class InputOutputDemo
{
public static void main(String[] args)
{
Res r=new Res(); //建立资源
Input in=new Input(r); //建立Input对象
Output out=new Output(r); //建立Output对象
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start(); //启动Input线程
t2.start(); //启动Output线程
}
}
9.2.2、运行结果
9.2.3、相关小知识
同步前提:
1、必须要有两个或者两个以上的线程;2、必须是多个线程使用同一个锁。
类的字节码文件对象:
静态进内存时,内存中没有本类对象,但是一定有"该类对应的字节码文件对象"(格式:“类名.class” 该对象的类型是Clas
等待唤醒机制:
(notify())(唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。 线程通过调用其中一个 wait 方法,在对象的监视器上等待。(用sleep()和yield()的时候锁并没有被释放,而调用wait()将释放锁 。这样另一个任务(线程)可以获得当 前对象的锁,从而进入它的synchronized方法中。可以通过notify()/notifyAll(),或者时间到期,从wait()中恢复执行。)
notify()与notifyAll()区别:
notify()用于唤醒在此对象锁上等待的单个线程
notifyAll()用于唤醒在此对象锁上等待的所有线程
notify()用于唤醒在此对象锁上等待的单个线程
notifyAll()用于唤醒在此对象锁上等待的所有线程
9.3、优化9.2代码
9.3.1、代码
class Res
{
private String name;
private String sex;
private boolean flag=false;
public synchronized void set(String name,String sex)
{
if (flag) //如果flag为true,本线程等待;否则就向下执行
try{this.wait();}catch (Exception e){}
this.name=name;
this.sex=sex;
flag=true;
this.notify();//传入的锁为this
}
public synchronized void out()
{
if(!flag)//如果!flag为true(即flag为false),本线程等待;否则就向下执行
try{this.wait();}catch (Exception e){}
System.out.println(name+"----"+sex);
flag=false;
this.notify();
}
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r=r;
}
public void run()
{
int x=0;
while (true)
{
if (x==0)
{
r.set("马克","男");
}
else
{
r.set("莉莉","女");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r=r;
}
public void run()
{
while (true)
{
r.out();
}
}
}
class InputOutputDemo2
{
public static void main(String[] args)
{
Res r=new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
9.3.2、运行结果
9.4、
生产者消费者实例(参考9.2优化后的程序)
当有多个生产者、消费者同时参与对资源的调用,依然仍让线程间按照一进一出的规律交互
9.4.1、代码
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)
{
//if(flag) //如果flag为true,本线程等待;否则就向下执行
while(flag)//②
{
try
{
this.wait();
}
catch (Exception e)
{
}
}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true;
//this.notify();
this.notifyAll();//③
}
public synchronized void out()
{
//if (!flag)//如果!flag为true(即flag为false),本线程等待;否则就向下执行
while(!flag)//②
{
try
{
this.wait();
}
catch (Exception e)
{
}
}
System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
flag=false;
//this.notify();
this.notifyAll();//③
}
}
// t1 t2
class Producer implements Runnable
{
Resource r=new Resource();
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while (true)
{
r.set("张三");
}
}
}
//t3 t4
class Consumer implements Runnable
{
Resource r=new Resource();
Consumer(Resource r)
{
this.r=r;
}
public void run()
{
while (true)
{
r.out();
}
}
}
class ProducerConsumerDemo
{
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();
}
}
9.4.2、运行结果
9.5、JDK1.5后提供的多线程升级解决方案,并应用到9.4生产者消费者示例中
JDK1.5 后提供了多线程升级解决方案:将同步Synchronized替换成显示Lock操作。将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以通过Lock锁进行获取。该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
具体见代码
9.5.1、代码
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(); 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 { Resource r=new Resource(); Producer(Resource r) { this.r=r; } public void run() { while (true) { try { r.set("张三"); } catch (Exception e) { } } } } class Consumer implements Runnable { Resource r=new Resource(); Consumer(Resource r) { this.r=r; } public void run() { while (true) { try { r.out(); } catch (Exception e) { } } } } 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(); } }
9.5.2、运行结果
10、停止线程
10.1、如何停止线程
stop方法已经过时,让线程run方法结束是解决的办法,这是因为程序开启多线程运行,运行代码通常是循环结构,通过控制循环,就可以让run
方法结束,也就是线程结束。
可是当线程处于了冻结状态。就无法读取到相应标记。那么线程不会结束。
参考代码:
class Stop implements Runnable
{
private boolean flag=true;
public void run()
{
while (flag)
{
System.out.println(Thread.currentThread().getName()+" run");
}
}
public void changeFlag()
{
flag=false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
Stop s=new Stop();
Thread t1=new Thread(s);
Thread t2=new Thread(s);
t1.start();//0线程启动
t2.start();//1线程启动
int num=0;
while (true) /*执行10次循环后,改变flag为false,让线程因为
while(false)而停止*/
{
if(num++==10)
{
s.changeFlag();
break;
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
}
运行结果:
为了解决线程因为冻结而无法结束的问题,Thread提供了interrupt方法
10.2、interrupt()方法
官方对interrupt方法的定义是:如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、
join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状
态将被清除,它还将收到一个 InterruptedException。
可理解为调
用interrupt方法可以强制清除线程的冻结状态,并使线程能继续向下执行,
参考代码:
class Stop implements Runnable
{
private boolean flag=true;
public synchronized void run()
{
while (flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+" Exception");
}
System.out.println(Thread.currentThread().getName()+" run");
flag=false;
}
}
public void changeFlag()
{
flag=false;
}
}
class StopThreadDemo2
{
public static void main(String[] args)
{
Stop s=new Stop();
Thread t1=new Thread(s);
Thread t2=new Thread(s);
t1.start();//0线程启动
t2.start();//1线程启动
int num=0;
while (true) /*执行10次循环后,改变flag为false,让线程因为
while(false)而停止*/
{
if(num++==10)
{
//s.changeFlag();//②
t1.interrupt(); //②
t2.interrupt(); //②
break;
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
}
10.3、setDaemon方法的应用
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
public final void setDaemon(boolean on)
参数:on - 如果为 true,则将该线程标记为守护线程。
抛出: IllegalThreadStateException - 如果该线程处于活动状态。
SecurityException - 如果当前线程无法修改该线程。
参数:on - 如果为 true,则将该线程标记为守护线程。
抛出: IllegalThreadStateException - 如果该线程处于活动状态。
SecurityException - 如果当前线程无法修改该线程。
11、线程类的其他方法
11.1、join()
如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都
执行完,A才会执行。join可以用来临时加入线程执行。如果线程B中断,则A线程也将无法执行
11.2、toString()
返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
优先级:线程的优先级总共10级,系统会为线程设置默认级别5(NORM_PRIORITY),1为最低(MIN_PRIORITY),10为最高(MAX_PRIORIT Y),可以通
过
setPriority()
设置线程优先级,优先级高并不一定先执行,只是表示被先执行的可能性大。
线程组:例如在主函数main()中产生一个线程,则产生的线程属于main这个线程组管理的一员。简单地说,线程组就是由线程组成的管理线 程的类
MAX_PRIORITY:"public static final int MAX_PRIORITY",在Thread类已被声明为静态方法,可直接通过Thread.MAX_PRIORITY调用
11.3、线程yield方法
MAX_PRIORITY:"public static final int MAX_PRIORITY",在Thread类已被声明为静态方法,可直接通过Thread.MAX_PRIORITY调用
11.3、线程yield方法
用于暂停当前正在执行的线程对象,并执行其他线程。
用处:用于降低当前线程被执行的频率,让其他线程也能被执行到