------<ahref="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
黑马程序员——12,多线程(2)
//线程之间的通信
//就是多个线程操作同一个资源,操作动作不同
//举一个例子:一堆资源(名字,性别),一个线程输入,一个线程输出打印
class Person
{
String name;
String sex;
}
class Shuru implements Runnable
{
private Person p;
Shuru( Person p )
{
this.p=p;
}
public void run()
{
int x= 0;
while(true)//特别说明:因为 while (true)这句话的关系,最后要按Ctrl+c按键才停下虚拟机,否则会耗费很多资源)
{
if(x==0)
{
p.name="张三";
p.sex="男性";
}
else
{
p.name="李四";
p.sex="女性";
}
x=(x+1)%2;
}
}
}
class Shuchu implements Runnable
{
private Person p;
Shuchu( Person p )
{
this.p=p;
}
public void run()
{
while(true)
{
System.out.println(p.name+"-----"+p.sex);
}
}
}
class Xctx
{
public static void main(String[] args)
{
Person p=new Person();
Shuru b1=new Shuru(p);
Shuchu b2=new Shuchu(p);
Thread c1=new Thread(b1);
Thread c2=new Thread(b2);
c1.start();
c2.start();
System.out.println("HelloWorld!");
}
}
/*
按照一般的思路,准确的打印应该是张三对应男性,李四对应女性的
可是编译运行的结果如下,出了问题:
Hello World!
李四-----男性
李四-----男性
李四-----女性
李四-----男性
张三-----女性
张三-----女性
李四-----男性
李四-----男性
张三-----男性
张三-----女性
张三-----男性
张三-----男性
李四-----男性
张三-----男性
李四-----女性
李四-----男性
张三-----男性
李四-----女性
张三-----女性
李四-----女性
李四-----男性
出问题的原因是:输入线程在输入信息的同时输出线程也在打印,
那么很有可能在输入信息输入到一半的时候,就被输出线程打印出去了,
那么打印出去的就是部分更新数据部分旧数据,这就对应不上了
*/
——————分析——————
//为了解决这种问题,我们还是可以用同步代码块来解决
/*
原理就是输入线程与输出线程都是对同一个对象操作,只不过操作过程不一样
那么使用同步代码块,让两个使用同一个锁,这样就限制了在某一个时刻只能够有一个线程对对象进行操作
等这个线程操作完之后下一个线程才可以对对象进行操作
确保了打印出来的对象信息的更新是完整
*/
class Person
{
String name;
String sex;
static Object obj=new Object();
}
class Shuru implements Runnable
{
private Person p;
Shuru( Person p )
{
this.p=p;
}
public void run()
{
int x= 0;
while(true)//特别说明:因为 while (true)这句话的关系,最后要按Ctrl+c按键才停下虚拟机,否则会耗费很多资源)
{
synchronized(Person.obj)//用的锁是obj
{
if(x==0)
{
p.name="张三";
p.sex="男性";
}
else
{
p.name="李四";
p.sex="女性";
}
x=(x+1)%2;
}
}
}
}
class Shuchu implements Runnable
{
private Person p;
Shuchu( Person p )
{
this.p=p;
}
public void run()
{
while(true)
{
synchronized(Person.obj)//用的锁是obj
{
System.out.println(p.name+"-----"+p.sex);
}
}
}
}
class Xctx2
{
public static void main(String[] args)
{
Person p=new Person();
Shuru b1=new Shuru(p);
Shuchu b2=new Shuchu(p);
Thread c1=new Thread(b1);
Thread c2=new Thread(b2);
c1.start();
c2.start();
System.out.println("HelloWorld!");
}
}
——————分析——————
</pre><pre name="code" class="java">//线程通信中的等待唤醒机制的例子
/*
等待唤醒机制,简单的说法就是
用一个标示(一般是Boolean型的变量)
true标记输入线程操作,而false标记输出线程操作
输入线程操作时候,输出线程冻结状态
输出线程操作时候,输入线程冻结状态
这样来保证输出结果的准确性
这就需要用到wait()和notify() 或者是notifyAll()
*/
class Person
{
String name;
String sex;
static Object obj=new Object();
boolean bs=true;//设定一个标示
}
class Shuru implements Runnable
{
private Person p;
Shuru( Person p )
{
this.p=p;//接收一个Person类的对象,就指向该对象,该写法更加方便
}
public void run()
{
int x= 0;
while(true)
//特别说明:因为 while (true)这句话的关系,最后要按Ctrl+c按键才停下虚拟机,否则会耗费很多资源)
{
synchronized(Person.obj)//用的锁是obj
{
if(!p.bs)
{
try{Person.obj.wait();}catch(Exception e){}
}
//wait()方法可以冻结线程,该方法是定义在Object类的
/*
这里为什么不直接用wait()呢?
因为wait()方法会抛出一个异常所以要用try...catch
为什么这里还要在wait前面加上Person.obj这个锁呢?
因为这种写法:锁.wait(); -----表示持有该锁的线程要陷入沉睡。
也是因为这个原因,wait()方法必须在同步中才可以用,而notify()
和notifyAll()也是因为以上原因在同步中才可以用。
其写法也是: 锁.notify();或者是 锁.notifyAll();
*/
if(x==0)
{
p.name="张三";
p.sex="男性";
}
else
{
p.name="李四";
p.sex="女性";
}
x=(x+1)%2;
Person.obj.notify();
/*
陷入沉睡的线程都会被扔进线程池
notify()方法功能是唤醒在在线程池中头一个沉睡的线程
而notifyAll()方法则是唤醒所有沉睡的线程
*/
p.bs=false;
}
}
}
}
class Shuchu implements Runnable
{
private Person p;
Shuchu( Person p )
{
this.p=p;
}
public void run()
{
while(true)
{
synchronized(Person.obj)//用的锁是obj
{
if(p.bs)
{
try{Person.obj.wait();} catch(Exception e){}
}
System.out.println(p.name+"-----"+p.sex);
Person.obj.notify();
p.bs=true;
}
}
}
}
class Xctx3
{
public static void main(String[] args)
{
Person p=new Person();
Shuru b1=new Shuru(p);
Shuchu b2=new Shuchu(p);
Thread c1=new Thread(b1);
Thread c2=new Thread(b2);
c1.start();
c2.start();
System.out.println("HelloWorld!");
}
}
/*
以上程序编译运行结果是:
Hello World!
张三-----男性
李四-----女性
张三-----男性
李四-----女性
张三-----男性
李四-----女性
张三-----男性
李四-----女性
张三-----男性
李四-----女性
张三-----男性
李四-----女性
张三-----男性
李四-----女性
*/
补充:对于非静态的synchronized方法,其锁默认为this
对于静态的synchronized方法,其锁默认:为该方法所在类的类名.class
对于同步代码块,其锁就是括号内的东西。
——————分割线————————
//以下是代码优化之后的程序,更加简洁明了
class Person
{
private String name;
private String sex;
private boolean bs=true;//设定一个标示
public synchronized void set( String name,String sex )//写入资料的方法
{
if(!bs)
{
try{this.wait();}catch(Exception e){}
}
this.name=name;
this.sex=sex;
bs=false;
this.notify();
}
public synchronized void out()//打印资料的方法
{
if(bs)
{
try{this.wait();}catch(Exception e){}
}
System.out.println("输出的是----"+name+"---"+sex);
bs=true;
this.notify();
}
}
class Shuru implements Runnable
{
private Person p;
Shuru( Person p )
{
this.p=p;
}
public void run()
{
int x= 0;
while(true)//特别说明:因为 while (true)这句话的关系,最后要按Ctrl+c按键才停下虚拟机,否则会耗费很多资源)
{
if(x==0)
p.set("张三","男性");
else
p.set("李四","女性");
x=(x+1)%2;
}
}
}
class Shuchu implements Runnable
{
private Person p;
Shuchu( Person p )
{
this.p=p;
}
public void run()
{
while(true)
{
p.out();
}
}
}
class Xctx4
{
public static void main(String[] args)
{
Person p=new Person();
newThread(new Shuru(p)).start();
new Thread(new Shuchu(p)).start();
System.out.println("HelloWorld!");
/*
Shuru b1=new Shuru(p);
Shuchu b2=new Shuchu(p);
Thread c1=new Thread(b1);
Thread c2=new Thread(b2);
c1.start();
c2.start();
System.out.println("HelloWorld!");
*/
}
}
————————分割线——————
/*
多线程的常见生活例子
有多个生产者生产食品,有多个消费者消化食品
每生产一个食品就消费一个食品
*/
class Shipin
{
private int sp=0;
private boolean f=true;
public synchronized void shengchang( )
{
while(true)
{
while(f==false)//注意这里用的是while所以每次线程从冻结状态醒来都要检查一次f是否是false
try{this.wait();} catch(Exception e){}
sp++;
System.out.println("生产者"+Thread.currentThread().getName()+"----"+sp);
f=false;
this.notifyAll();
/*
这里用notifyAll()方法就是为了让所有沉睡的线程醒来
既唤醒了生产者又唤醒了消费者
此时如果f=true那么由于之前的while作用,消费者即使是得到权限(锁)也不能执行只能冻结,所以只有一个生产者可以得到权限执行。
此时如果是f=false那么由于之前的while作用,生产者即使是得到权限(锁)也不能执行只能冻结所以只有一个消费者可以得到权限执行。
*/
}
}
public synchronized void xiaofei( )
{
while(true)
{
while(f==true)
try{this.wait();} catch(Exception e){}
System.out.println("消费者"+Thread.currentThread().getName()+"----"+sp);
f=true;
this.notifyAll();
}
}
}
class Shengchangzhe implements Runnable
{
private Shipin a ;
Shengchangzhe(Shipin a)
{
this.a=a;
}
public void run()
{
a.shengchang();
}
}
class Xiaofeizhe implements Runnable
{
private Shipin a ;
Xiaofeizhe(Shipin a)
{
this.a=a;
}
public void run()
{
a.xiaofei();
}
}
class Xctx5
{
public static void main(String[] args)
{
Shipin a=new Shipin();
Shengchangzhe b1=new Shengchangzhe(a);
Shengchangzhe b2=new Shengchangzhe(a);
Xiaofeizhe b3=new Xiaofeizhe(a);
Xiaofeizhe b4=newXiaofeizhe(a);
Thread t1=new Thread(b1);
Thread t2=new Thread(b2);
Thread t3=new Thread(b3);
Thread t4=new Thread(b4);
t1.start();
t2.start();
t3.start();
t4.start();
System.out.println("HelloWorld!");
}
}
/*
以上程序编译运行结果如下:
生产者Thread-0----1
Hello World!
消费者Thread-3----1
生产者Thread-1----2
消费者Thread-2----2
生产者Thread-0----3
消费者Thread-3----3
生产者Thread-1----4
消费者Thread-2----4
生产者Thread-0----5
消费者Thread-3----5
生产者Thread-1----6
消费者Thread-2----6
生产者Thread-0----7
消费者Thread-3----7
生产者Thread-1----8
消费者Thread-2----8
生产者Thread-0----9
消费者Thread-3----9
生产者Thread-1----10
消费者Thread-2----10
生产者Thread-0----11
消费者Thread-3----11
生产者Thread-1----12
消费者Thread-2----12
生产者Thread-0----13
消费者Thread-3----13
生产者Thread-1----14
消费者Thread-2----14
生产者Thread-0----15
消费者Thread-3----15
生产者Thread-1----16
消费者Thread-2----16
生产者Thread-0----17
消费者Thread-3----17
生产者Thread-1----18
消费者Thread-2----18
生产者Thread-0----19
消费者Thread-3----19
生产者Thread-1----20
消费者Thread-2----20
生产者Thread-0----21
消费者Thread-3----21
生产者Thread-1----22
消费者Thread-2----22
生产者Thread-0----23
消费者Thread-3----23
生产者Thread-1----24
消费者Thread-2----24
生产者Thread-0----25
消费者Thread-3----25
生产者Thread-1----26
消费者Thread-2----26
生产者Thread-0----27
消费者Thread-3----27
生产者Thread-1----28
消费者Thread-2----28
生产者Thread-0----29
消费者Thread-3----29
生产者Thread-1----30
消费者Thread-2----30
生产者Thread-0----31
*/
——————分割线————
//另外一个是jdk1.5版本升级后的情况:
import java.util.concurrent.locks.*;
//这个导入的是后面需要用到的类
/*
随着jdk1.5版本的升级,一些新的功能也被加入,更加的方便使用者
就拿多线程通信的生产者消费者例子来说
*/
class Xctx6
{
public static void main(String[] args)
{
Shipin a=new Shipin();
Shengchangzhe b1=new Shengchangzhe(a);
Xiaofeizhe b2=new Xiaofeizhe(a);
Thread t1=new Thread(b1);
Thread t2=new Thread(b1);
Thread t3=new Thread(b2);
Thread t4=new Thread(b2);
t1.start();
t2.start();
t3.start();
t4.start();
System.out.println("HelloWorld!");
}
}
class Shipin
{
private int sp=0;
private boolean f=true;
private Lock lock=new ReentrantLock();//将锁作为一个对象了
private Condition pro= lock.newCondition();
private Condition con= lock.newCondition();
/*
Condition将Object监视器(锁)的方法(wait,notify,notifyAll)分解成不同的对象,
这是为了便于与Lock组合使用。
Lock代替synchronized方法和语句的使用(同步函数和同步代码块)
Condition替代了Object监视器(锁)的方法的使用。
Condition实例是绑定在锁上的,一个Lock实例要获得Condition实例就要调用newCondition方法。
*/
public void shengchang() throws InterruptedException
{
lock.lock();
try
{
while(f==false)
pro.await();//生产者线程陷入冻结
//这个句式会抛出一个InterruptedException异常
sp++;
System.out.println("生产者"+Thread.currentThread().getName()+"----"+sp);
f=false;
con.signal();//仅仅唤醒消费者线程
}
finally
{
lock.unlock();//释放锁
}
/*
这里是所以要使用try...finally句型是因为确保
一定要执行 lock.unlock();也是因为前面的pro.await();会向外抛出
一个异常,如果没有这个句型程序就会跳出去而没有
执行lock.unlock();这样的话线程就没有释放锁
*/
}
public void xiaofei() throws InterruptedException
{
lock.lock();
try
{
while(f==true)
con.await();//消费者线程陷入冻结
System.out.println("消费者"+Thread.currentThread().getName()+"----"+sp);
f=true;
pro.signal();//唤醒生产者线程
}
finally
{
lock.unlock();
}
}
}
class Shengchangzhe implements Runnable
{
private Shipin a ;
Shengchangzhe(Shipin a)
{
this.a=a;
}
public void run()
{
while(true)
{
try{a.shengchang();}
catch(Exception e){}
}
}
}
class Xiaofeizhe implements Runnable
{
private Shipin a ;
Xiaofeizhe(Shipin a)
{
this.a=a;
}
public void run()
{
while(true)
{
try{a.xiaofei();}
catch(Exception e){}
}
}
}
/*
对前面的例子做了一个改进:
有生产者,检查者,消费者
先要由生产者生产食品,
然后由检查者检测食品,
最后由消费者消化食品。
*/
import java.util.concurrent.locks.*;
class Xctx7
{
public static void main(String[] args)
{
Shipin a=new Shipin();
Shengchangzhe b1=new Shengchangzhe(a);
Xiaofeizhe b2=new Xiaofeizhe(a);
Jiancezhe b3=new Jiancezhe(a);
Thread t1=new Thread(b1);
Thread t2=new Thread(b1);
Thread t3=new Thread(b2);
Thread t4=new Thread(b2);
Thread t5=new Thread(b3);
Thread t6=new Thread(b3);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
System.out.println("HelloWorld!");
}
}
class Shipin
{
private int sp=0;
private int f=1;
private Lock lock=new ReentrantLock();//将锁作为一个对象了
private Condition pro= lock.newCondition();
private Condition con= lock.newCondition();
private Condition jc = lock.newCondition();
public void shengchang() throws InterruptedException
{
lock.lock();
try
{
while(f!=1)
{
pro.await();//生产者线程陷入冻结
}
sp++;
System.out.println("生产者"+Thread.currentThread().getName()+"----"+sp);
f=2;
jc.signal();//唤醒检测者线程
}
finally
{
lock.unlock();//释放锁
}
}
public void xiaofei() throws InterruptedException
{
lock.lock();
try
{
while(f!=3)
{
con.await();//消费者线程陷入冻结
}
System.out.println("消费者"+Thread.currentThread().getName()+"----------"+sp);
f=1;
pro.signal();//唤醒生产者线程
}
finally
{
lock.unlock();
}
}
public void jiance() throws InterruptedException //检测方法
{
lock.lock();
try
{
while(f!=2)
{
jc.await();//检测者线程陷入冻结
}
System.out.println("检测者"+Thread.currentThread().getName()+"-------"+sp);
f=3;
con.signal();//唤醒消费者线程
}
finally
{
lock.unlock();
}
}
}
class Shengchangzhe implements Runnable
{
privateShipin a ;
Shengchangzhe(Shipin a)
{
this.a=a;
}
public void run()
{
while(true)
{
try{a.shengchang();}
catch(Exception e){}
}
}
}
class Xiaofeizhe implements Runnable
{
private Shipin a ;
Xiaofeizhe(Shipin a)
{
this.a=a;
}
public void run()
{
while(true)
{
try{a.xiaofei();}
catch(Exception e){}
}
}
}
class Jiancezhe implements Runnable
{
private Shipin a ;
Jiancezhe(Shipin a)
{
this.a=a;
}
public void run()
{
while(true)
{
try{a.jiance();}
catch(Exception e){}
}
}
}
/*
以上的代码编译运行结果如下:
生产者Thread-0----1
Hello World!
检测者Thread-4-------1
消费者Thread-3----------1
生产者Thread-0----2
检测者Thread-5-------2
消费者Thread-3----------2
生产者Thread-1----3
检测者Thread-5-------3
消费者Thread-2----------3
生产者Thread-0----4
检测者Thread-4-------4
消费者Thread-3----------4
生产者Thread-1----5
检测者Thread-5-------5
消费者Thread-2----------5
生产者Thread-0----6
检测者Thread-4-------6
消费者Thread-3----------6
生产者Thread-1----7
检测者Thread-5-------7
消费者Thread-2----------7
生产者Thread-0----8
检测者Thread-4-------8
消费者Thread-3----------8
生产者Thread-1----9
检测者Thread-5-------9
消费者Thread-2----------9
生产者Thread-0----10
检测者Thread-4-------10
消费者Thread-3----------10
生产者Thread-1----11
检测者Thread-5-------11
消费者Thread-2----------11
生产者Thread-0----12
检测者Thread-4-------12
消费者Thread-3----------12
生产者Thread-1----13
检测者Thread-5-------13
消费者Thread-2----------13
生产者Thread-0----14
检测者Thread-4-------14
消费者Thread-3----------14
生产者Thread-1----15
检测者Thread-5-------15
消费者Thread-2----------15
生产者Thread-0----16
检测者Thread-4-------16
消费者Thread-3----------16
生产者Thread-1----17
检测者Thread-5-------17
消费者Thread-2----------17
生产者Thread-0----18
检测者Thread-4-------18
消费者Thread-3----------18
生产者Thread-1----19
检测者Thread-5-------19
消费者Thread-2----------19
生产者Thread-0----20
检测者Thread-4-------20
消费者Thread-3----------20
生产者Thread-1----21
检测者Thread-5-------21
消费者Thread-2----------21
生产者Thread-0----22
检测者Thread-4-------22
消费者Thread-3----------22
生产者Thread-1----23
检测者Thread-5-------23
消费者Thread-2----------23
生产者Thread-0----24
检测者Thread-4-------24
消费者Thread-3----------24
生产者Thread-1----25
检测者Thread-5-------25
消费者Thread-2----------25
生产者Thread-0----26
检测者Thread-4-------26
消费者Thread-3----------26
生产者Thread-1----27
检测者Thread-5-------27
消费者Thread-2----------27
生产者Thread-0----28
检测者Thread-4-------28
消费者Thread-3----------28
生产者Thread-1----29
检测者Thread-5-------29
消费者Thread-2----------29
生产者Thread-0----30
检测者Thread-4-------30
消费者Thread-3----------30
生产者Thread-1----31
检测者Thread-5-------31
消费者Thread-2----------31
生产者Thread-0----32
检测者Thread-4-------32
消费者Thread-3----------32
生产者Thread-1----33
检测者Thread-5-------33
消费者Thread-2----------33
生产者Thread-0----34
检测者Thread-4-------34
消费者Thread-3----------34
生产者Thread-1----35
检测者Thread-5-------35
消费者Thread-2----------35
生产者Thread-0----36
检测者Thread-4-------36
消费者Thread-3----------36
生产者Thread-1----37
检测者Thread-5-------37
消费者Thread-2----------37
生产者Thread-0----38
检测者Thread-4-------38
消费者Thread-3----------38
生产者Thread-1----39
检测者Thread-5-------39
消费者Thread-2----------39
生产者Thread-0----40
检测者Thread-4-------40
消费者Thread-3----------40
生产者Thread-1----41
检测者Thread-5-------41
消费者Thread-2----------41
生产者Thread-0----42
检测者Thread-4-------42
消费者Thread-3----------42
生产者Thread-1----43
检测者Thread-5-------43
消费者Thread-2----------43
生产者Thread-0----44
检测者Thread-4-------44
消费者Thread-3----------44
生产者Thread-1----45
检测者Thread-5-------45
消费者Thread-2----------45
生产者Thread-0----46
检测者Thread-4-------46
消费者Thread-3----------46
生产者Thread-1----47
检测者Thread-5-------47
消费者Thread-2----------47
生产者Thread-0----48
检测者Thread-4-------48
消费者Thread-3----------48
生产者Thread-1----49
检测者Thread-5-------49
消费者Thread-2----------49
生产者Thread-0----50
检测者Thread-4-------50
消费者Thread-3----------50
生产者Thread-1----51
检测者Thread-5-------51
消费者Thread-2----------51
生产者Thread-0----52
检测者Thread-4-------52
消费者Thread-3----------52
生产者Thread-1----53
检测者Thread-5-------53
消费者Thread-2----------53
生产者Thread-0----54
检测者Thread-4-------54
消费者Thread-3----------54
生产者Thread-1----55
检测者Thread-5-------55
消费者Thread-2----------55
生产者Thread-0----56
检测者Thread-4-------56
消费者Thread-3----------56
生产者Thread-1----57
检测者Thread-5-------57
消费者Thread-2----------57
生产者Thread-0----58
检测者Thread-4-------58
消费者Thread-3----------58
生产者Thread-1----59
检测者Thread-5-------59
消费者Thread-2----------59
生产者Thread-0----60
检测者Thread-4-------60
消费者Thread-3----------60
生产者Thread-1----61
检测者Thread-5-------61
消费者Thread-2----------61
生产者Thread-0----62
检测者Thread-4-------62
消费者Thread-3----------62
生产者Thread-1----63
检测者Thread-5-------63
消费者Thread-2----------63
*/