您的位置:首页 > 职场人生

黑马程序员--Java基础之多线程(2)

2013-11-25 15:11 162 查看
------------------
android培训、java培训、期待与您交流! ---------------------
四、线程间通信---等待唤醒机制

1、线程间通信:就是多个线程在操作同一个资源,但是操作的动作不同。

如生产者和消费者的例子,比如轮流对人对象姓名年龄的输入和输出动作。

2、线程间的通信其实就是---不同动作间的同步问题!

操作同一资源的代码分别在不同的地方,要将他们同步在一起,并且还要轮流操作。

比如对人对象进行姓名年龄的输入和输出动作,输入动作和输出动作分别在不同的线程中,

而且这两个操作必须同步,还要轮流进行操作。

3、解决办法:使用同步Synchronized,用同一个锁使不同动作同步(可以使用要操作的同一个对象作为锁)。

使用flag标记位,通过判断标记位决定是否可以操作,结合wait方法等待,notify方法唤醒单个线程,

notifyAll方法唤醒所有等待的线程,实现不同动作的轮流操作。

wait(),notify(),notifyAll()方法必须使用在同步中,且必须拥有同步的锁。

4、示例代码:(轮流对人对象姓名年龄的输入和输出动作)

public class ThreadCommunication {

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();

}

}
class Person{
private String name;
private String sex;
private boolean flag = false;

//设置姓名和性别的方法set
public synchronized void set(String name,String sex){
while(flag){//如果flag为true说明已经设置好姓名和性别,设置操作需要等待一下。
//如果flag为false说明设置的东西已经被取走,可以进行设置操作了,则继续往下走。
//这里使用while方法,不适用if方法,这是因为while循环使线程被唤醒时依然会判断一下标记!
try {this.wait();} catch (InterruptedException e){}//同步函数的锁是this
}
this.name = name;
this.sex = sex;

flag = true;//设置完毕,将标志位设置为true,表示已经设置完毕
this.notifyAll();//设置完毕唤醒其他线程
//this.notify();//notify()方法会造成死锁,因为它通常会唤醒线程池中第一个wait的方法。
}

//打印姓名和性别的方法get
public synchronized void get(){
while(!flag){
try {this.wait();} catch (InterruptedException e) {}
}
System.out.println(this.name+"-------------"+this.sex);

flag = false;
this.notifyAll();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}

class Input implements Runnable{
private Person p;
private int x = 1;
Input(Person p){
this.p = p;
}
@Override
public void run() {
while(true){
if(x==1){
p.set("丽丽","女");
x++;
}
else{
p.set("John","man");
x--;
}
}
}
}

class Output implements Runnable{
private Person p;
Output(Person p){
this.p = p;
}
@Override
public void run() {
while(true){
p.get();
}
}
}


5、notify方法会造成死锁,
这是因为,等待的线程都按顺序存放在线程池中,notify通常唤醒线程池中的第一个线程,
也就是第一个wait的线程,这回造成死锁。notifyAll()方法用来解决死锁问题。但是对方线程会抢占资源。
6、JDK1.5改进:Lock:lock,unlock。 Condition,await,signal。显式的锁机制,以及显式的锁上的等待唤醒机制。
JDK1.5中提供了多线程升级解决方案,将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify,notifyAll 替换成了condition对象。可以通过Lock的锁,获取condition对象。

下面是多个生产者和消费者的代码示例:

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

public class ProAndCon {
public static void main(String[] args){
Resource res = new Resource();
Producer pro = new Producer(res);
Consumer con = new Consumer(res);
Thread t1 = new Thread(pro);//制造多个生产者和消费者
Thread t2 = new Thread(pro);
Thread t3 = new Thread(pro);
Thread t4 = new Thread(con);
Thread t5 = new Thread(con);
Thread t6 = new Thread(con);

t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}

class Resource{
private int num;
private boolean flag = false;
private Lock lock = new ReentrantLock();//定义一个锁,它的锁和解锁操作代替了同步操作
private Condition pro_condition = lock.newCondition();//生产者的
private Condition con_condition = lock.newCondition();//消费者的

public void set(){
lock.lock();//首先上锁
try {
while(flag){
pro_condition.await();//如果有产品,生产者wait
}
++num;
System.out.println(Thread.currentThread().getName()+"生产的第"+num+"个产品");
flag = true;//生产后设置标记位为true
con_condition.signalAll();//唤醒所有消费者
} catch (InterruptedException e) {
}finally{
lock.unlock();//最后别忘了解锁,解锁放在finally语句中,确保一定会执行解锁
}
}

public void get(){
try {
lock.lock();
while(!flag){
con_condition.await();
}

System.out.println(Thread.currentThread().getName()+"消费了第+++++++++++"+num+"个产品+++++++++++");
flag = false;
pro_condition.signalAll();//唤醒所有生产者
} catch (InterruptedException e) {

}finally{
lock.unlock();
}
}

}

class Producer implements Runnable{
private Resource res ;
Producer(Resource res){
this.res = res;
}
@Override
public void run() {
while(true){
res.set();
}
}

}

class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res = res;
}
@Override
public void run() {
while(true){
res.get();
}
}
}


五、停止线程、守护线程、线程优先级以及join方法yield方法

1、停止线程
stop()方法已经过时,一般代码运行完毕,该线程就结束了。
特殊情况:循环状态下,当线程处于冻结状态,就不会读取标记,也就不能结束线程。这时可以使用Thread类提供的方法 interrupt();对冻结清楚,然后修改标记,使循环结束。
2、守护线程
setDaemon (true)将该线程设置为守护线程。
守护线程又称后台线程,后台线程依赖于前台线程,前台线程只要一结束,后台线程也就随之结束。
注意:该方法必须在启动线程前调用。当正在运行的线程都是守护线程时,Java虚拟机退出,程序结束。
3、线程优先级
线程优先级共有是个级别:1-10 。
一般分为三个层次:MAX_PRIORITY(10);MIN_PRIORITY(1);NORM_PRIORITY(5)。
可以通过setPriority(int newPriority);来改变线程的优先级状态。
4、join方法,yield方法。
join(),等待该线程终止。当A线程执行到了B线程的join方法时,A就会等待,等B线程都执行完,A才会执行。
Join()可以用来临时加入线程执行。
yield(),暂停当前正在执行的线程对象,并执行其他线程。临时释放执行权,有利于线程的平均运行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: