A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 18027587106 初级黑马   /  2015-8-11 11:58  /  168 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

在Java中,前期普通程序都是单线程的例子,单线程是main()方法函数(当然除去jvm虚拟机运行时的线程)。为什么我们需要多线程呢?
1:增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
2:多线程有助于提高程序的安全性,比如QQ,在程序运行时都会有守护线程,保证线程在运行时信息不会被窃    取。
3:对数据操作,当我们对数据库实现操作时,我们可以对两条并行的数据资料实现并行操作,不需要等前面的    数据运行完之后再接着运行,这样影响效率。多线程可以实现并发同时运行,使代码之间的耦合度降低。
具体看多生产多消费的例子:
Goods 类:
package cn.itcast.sh.b_Mlock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Goods {
private String[] str=new String[1];//定义对goods商品的定义,只有一个商品
int num=0;//定义生产者生产的数量或者消费者消费的数量
//private Object obj=new Object();
Lock loc=new ReentrantLock();//使用Lock类来定义锁
Condition p=loc.newCondition();//定义生产者的线程监视器
Condition c=loc.newCondition();//定义消费者的线程监视器
void add( ){
loc.lock();//加和消费者一样的锁loc
try {
//判断goods类是否有商品,如果有商品我们应该让生产者的监视器线程处于等待状态
while(str[0] !=null){
try {
p.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
str[0]="面包";
System.out.println(Thread.currentThread().getName()+" 存储了.."+str[0]+"->"+ (++num));
c.signalAll();//通知消费者来消费,让消费者线程出等待状态
}
finally{
loc.unlock();//不管生产者代码是否发生异常,解锁对锁的绑定
}
}
void get(  ){
loc.lock();//加锁后面的同步代码
try {
//如果商品为空,我们让消费者线程等待消费
while(str[0]==null){
try {
c.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//如果有商品,正常执行消费者代码
System.out.println(Thread.currentThread().getName()+"取出了"+str[0]+"-->"+num);
str[0]=null;//取出商品之后重置goods类中的商品
p.signalAll();//通知生产者线程生产商品

}
finally{
loc.unlock();//不管是否加锁代码是否发生异常,都需要解锁,防止死锁
}
}

}


Customer类:

package cn.itcast.sh.b_Mlock;

public class Customer implements Runnable {

Goods g;
int num;
Customer(Goods g){
this.g=g;
}
//消费者线程的执行代码
public void run() {
//同样让消费者也一直消费
while(true){
g.get();
}
// System.out.print(".."+num);
// System.out.println();
//num--;
}
}

Produce 类:

package cn.itcast.sh.b_Mlock;

public class Produce implements Runnable {

Goods g;
int num=1;
//生产者一创建对象就需要明确对那个goods类进行操作,所以我们需要在生产者的构造
//方法中添加goods类对象的参数
Produce(Goods g){
this.g=g;
}
//定义生产者的线程执行过程
public void run(  ) {
     //让生产者一直循环生产
while(true){
g.add();
}
// System.out.print(".."+num);
// System.out.println();
//num ++ ;
}

}

最后一个运行类:Test

package cn.itcast.sh.b_Mlock;
public class Test {

public static void main(String[] args) {

Goods g=new Goods();//首先初始化goods类中的商品
Produce pro=new Produce(g);//定义生产者
Customer cus=new Customer(g);//定义消费者
Thread tt=new Thread(pro);//定义生产者线程
Thread tt2=new Thread(pro);
Thread tt3=new Thread(cus);
Thread tt4=new Thread(cus);//定义消费者线程
tt.start();//开启生产线程
tt2.start();
tt3.start();
tt4.start();//开启消费者线程
}

}

Java <wbr>多线程(多生产多消费例子)


个人说明:
在各个代码块中有详细的说明。

注意事项:
1:多线程因为是并发运行相同代码块的,他可以提高代码的利用率,增加程序的健壮性,但是因为它是运行相同的代码块,所以会出现安全问题,由于电脑CPU在运行多线程时会在不同的线程之间来回切换,导致最后数据的不一致。所以我们会在线程中加锁:
锁的使用我目前只会两种:
Synchornized(对象){}和Lock类;当然Synchronized关键字可以加在代码块上,可以加在方法中,还可以加在静态代码块上。直接加在代码块上面可以随意指定对象,加在方法中一般加锁的对象为调用此方法的对象,加在静态代码块中的锁用这个方法所在类的class文件。
2:在多生产多消费的例子中,我们还要注意的是while()和if()的区别。可以看Goods类中的例子,这里为什么不用if而用while,相信大家看看便知;
3:这个程序的写作流程:
可以看到,这个程序中最直接的是Goods类,所有的逻辑代码块都在goods类中,首先我们需要明确的是对什么操作,生产者和消费者是做什么东西的?这样就产生了Goods类,在生产者和消费者的构造方法中我们就需要明确参数.
接下来因为是有生产者和消费者两个不同操作的线程,所有接下来我们要考虑到底是有那个类来继承runnable类呢?
如果我们直接用Goods类来继承可不可以?如果让Goods类来继承我们该怎么写这个run()方法呢?因为run方法中牵涉到对同一个对象相反的操作,所以我们只能放在生产者和消费者中来继承这个runnable类。
最后就是在Goods类中书写生产者和消费者的方法。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马