在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类中书写生产者和消费者的方法。 |
|