`

生产者与消费者问题 线程基础篇

    博客分类:
  • J2EE
阅读更多
在程序中的多个线程一般是为了完成一个或一些共同的目标而同时存在的,所以线程之间常需要共享内存等资源(相同对象或变量),若不对线程进行协调,则有可能出现资源冲突。多线程同步处理的目的是为了让多个线程协调地并发工作。

实际编程遇到的困惑?
多个线程引用同一个实例对象,线程调用该对象的同一方法,需不需要排队,还是并发?
实验结果:并发
若对实例对象方法使用关键字synchronized,则需要排队。
反过来来想,如果排队,还需要同步,呵呵。
例如一个线程对a加1操作100次,另一个线程对b减1操作100次,两个线程同时启动,结果
不为0,两个操作同时竞争的时候可能使一个操作失效,如+1的过程,1还没加上去,-1就把
控制权拿去了,+1操作就丢了。

线程常涉及的若干方法
Object
        notify() //唤醒在此对象监视器上等待的单个线程
        notifyAll() //唤醒在此对象监视器上所有等待的所有线程
        wait() //导致当前线程等待,由notify(),notifyAll()唤醒
Thread
        Thread()
        Thread(Runable target)
        Thread(Runable target, String name)
        currentThread()         //返回当前正在执行的线程对象的引用
        getId() //返回该线程的线程号
        getName() //返回该线程的名称
        interrupt() //中断线程
        interrupted()         //测试该线程是否已经中断 静态方法
        isAlive() //测试该线程是否处于活动状态
        isInterrupted()  //测试该线程是否已经中断
        join() //等待该线程终止
        sleep() //睡眠
        start() //启动线程
        yield() //暂停当前正在执行的线程对象,并执行其他线程 静态方法
Runnable
        run()

创建线程
public class ThreadTest extends Thread {

    public void run() {
        System.out.println("@run() 子线程号:" + Thread.currentThread().getId());
        for(int i=0; i<5; i++)
            System.out.println("i=" + i);
    }   
    
    public static void main(String[] args) {
        ThreadTest tt = new ThreadTest();
        tt.start();
        System.out.println("@main() 主程序的线程号:" + Thread.currentThread().getId());
    }
}


public class RunnableTest implements Runnable {

    public void run() {
        System.out.println("@run() 子线程号:" + Thread.currentThread().getId());
        for(int i=0; i<5; i++)
            System.out.println("i=" + i);
    }

    public static void main(String[] args) {
        Thread t = new Thread(new RunnableTest());
        t.start();
        System.out.println("@main() 主程序的线程号:" + Thread.currentThread().getId());
    }
}




线程并发控制带来的问题
两个以上线程并发访问共享变量,假设线程A取数据,线程B修改数据,
那么A取得数据可能已经被B修改过,所以需要对共享变量进行控制

多线程同步的原理
共享变量为类的属性,所以JVM通过给每个对象加锁实现多线程的同步处理。
对象A包括实例对象和类对象
实例对象 A a = new A();
类对象  a.getClass()  Class.forName("a")   A.class
一个类对应一个类对象,类对象在要使用类的时候加载,A.java编译生成A.class,将A.class加载到内存就是类对象
类对象具体参考:
http://www.iteye.com/topic/355046

JVM为每个对象配置一把锁(lock)和一个等候集(wait set)。
对象内部锁定只同步语句块和同步方法,一锁则全锁 (全部同步语句块和同步方法
asynchronized(asyncObject) {}
public asynchronized void print() {}
JVM上锁:
       1.判断上锁对象类型
            1.1 实例对象,锁定所有非静态同步方法和同步语句块
                public asynchronized void print()
            1.2 类对象,锁定所有静态同步方法和同步语句块
                public static asynchronized void print()

import java.util.Date;

class AsyncObj {
    public synchronized static void asyncStatic1() throws InterruptedException {
        System.out.println("进入asyncStatic1    " + new Date());
        Thread.sleep(2000);
        System.out.println("离开asyncStatic1    " + new Date());
    }

    public synchronized static void asyncStatic2() throws InterruptedException {
        System.out.println("进入asyncStatic2    " + new Date());
        Thread.sleep(2000);
        System.out.println("离开asyncStatic2    " + new Date());
    }

    public synchronized void async1() throws InterruptedException {
        System.out.println("进入async1    " + new Date());
        Thread.sleep(2000);
        System.out.println("离开async1    " + new Date());
    }

    public synchronized void async2() throws InterruptedException {
        System.out.println("进入async2    " + new Date());
        Thread.sleep(2000);
        System.out.println("离开async2    " + new Date());
    }
    
    public void nonAsync() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("进入nonAsync    " + new Date());
        System.out.println("离开nonAsync    " + new Date());
    }
    
    public static void nonAsyncStatic() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("进入nonAsyncStatic    " + new Date());
        System.out.println("离开nonAsyncStatic    " + new Date());
    }

}

public class AsyncTest extends Thread {
    public AsyncObj sharedObj;

    public int asyncTestThreadId;

    public AsyncTest(int asyncTestThreadId, AsyncObj sharedObj) {
        this.sharedObj = sharedObj;
        this.asyncTestThreadId = asyncTestThreadId;
    }

    public void run() {
        try {
            switch(asyncTestThreadId) {
            case 0:
                sharedObj.async1();     //锁定实例对象
                break;
            case 1:
                sharedObj.async2();
                break;
            case 2:
                sharedObj.nonAsync();
                break;
            case 3:
                AsyncObj.asyncStatic1();  //锁定类对象
                break;
            case 4:
                AsyncObj.asyncStatic2();
                break;
            case 5:
                AsyncObj.nonAsyncStatic();
                break;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        AsyncObj sharedObj = new AsyncObj(); // 被访问的实例对象
        AsyncTest[] threads = new AsyncTest[6]; // 多个访问线程
        for (int i = 0; i < 6; i++) {
            threads[i] = new AsyncTest(i, sharedObj);
            threads[i].start();
        }
    }

}




程序运行:
t=0
asyncStatic1获得类对象的锁 睡两秒钟
async1获得实例对象的锁 睡两秒钟
t=1
nonAsyncStatic,nonAsync睡醒执行,无所畏惧,锁不住它们
t=2
asyncStatic1,async1睡醒执行完代码退出,释放相应的锁
asyncStatic2,async2获得锁进入
t=4
asyncStatic2,async2睡醒执行完代码退出,释放相应的锁


//消费者与生产者例子介绍 wait(),notify(),notifyAll()
//wait(),notify(),notifyAll()只能在同步方法或同步控制块中调用

class Disk {
    private int apple; // number=1 有苹果 number=0 无苹果
    
    public Disk(int apple) {
        this.apple = apple;
    }
    
    //消费者调用消费苹果
    public synchronized void takeApple() {
        String name = Thread.currentThread().getName() + "线程: ";
        if (apple == 0)
            try {
                System.out.println(name + "盘子里面的苹果为空,等待生产者生产");
                wait();                 //消费者释放锁,使生产者能够进入addApple()
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        System.out.println(name + "盘子为满,开始消费一个苹果 花费5秒");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + "盘子为满,结束消费一个苹果");
        apple--;
        System.out.println(name + "唤醒生产者");
        notifyAll();
    }

    //生产者调用生产苹果
    public synchronized void addApple() {
        String name = Thread.currentThread().getName() + "线程: ";
        if (apple == 1)
            try {
                System.out.println(name + "盘子为满,等待消费者消费");
                wait();             //生产者释放锁,使消费者能够进入takeApple()
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        System.out.println(name + "盘子为空,开始生产一个苹果 花费3秒");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + "盘子为空,结束生产一个苹果");
        apple++;
        System.out.println(name + "唤醒消费者");
        notifyAll();
    }

}
class Producer implements Runnable {
    private Disk d;

    public Producer(Disk d) {
        this.d = d;
    }

    public void run() {
        while (true) {
            d.addApple();
        }
    }
}
class Consumer implements Runnable {
    private Disk d;

    public Consumer(Disk d) {
        this.d = d;
    }

    public void run() {
        while (true) {
            d.takeApple();
        }
    }
}
public class TestPC {
    public static void main(String[] args) {
        Disk d = new Disk(0);   //初始化盘子为0个苹果
        new Thread(new Consumer(d),"Consumer").start();     //启动消费者线程
        new Thread(new Producer(d),"Producer").start();     //启动生产者线程
    }
}

同步情况下
sleep()   使当前线程暂停执行一段时间,不释放对象锁,其他线程仍旧无法访问共享变 量
wait()    使当前线程暂停执行,释放对象锁标志,该线程被置于等候集,notify()notifyAll(),或者等待超时,激活线程进入就绪态,执行从wait()下一句开始
yield()   作用同sleep()类似,无法指定睡眠时间





1
0
分享到:
评论

相关推荐

    QT/C++多线程练习:单生产者多消费者(源码)

    主线程、生产者线程(一)、消费者管理线程(一)、消费者线程(多),以及一些全局变量。 主线程职责:UI控件的控制和显示、控制生产者:调节生产的速度和暂停与继续、控制消费 生产者线程职责:根据商品数量调控...

    java生产者与消费者问题

    生产者与消费者问题是多线程编程中的一个经典问题,它主要涉及到线程间的协作与同步。在Java中,我们可以利用其内置的并发工具类来解决这个问题。本篇将深入探讨如何使用Java实现生产者和消费者的模型,并通过具体的...

    生产者与消费者问题.docx

    在本篇文章中,我们将重点探讨使用Java语言实现生产者与消费者问题的几种方法。 #### 二、生产者与消费者问题描述 在一个典型的生产者与消费者模型中,存在两个角色:生产者和消费者。生产者负责生成数据并将这些...

    河南大学操作系统期中论文(生产者消费者问题详细分析)

    操作系统中的“生产者消费者问题”是一个...通过这篇论文,读者可以深入理解生产者消费者问题的理论基础,学习到如何在实际操作系统的环境中实现有效的线程同步,这对于理解并发编程和设计高效的多线程系统至关重要。

    生产者和消费者问题源程序

    ### 生产者与消费者问题详解 #### 一、引言 在计算机科学中,生产者-消费者问题是进程间通信的经典案例之一,它涉及到资源管理和同步的问题。本篇将基于一份来自中山大学的实验代码,深入解析生产者-消费者模型的...

    Java多线程编程实战指南 设计模式篇.rar

    1. 生产者消费者模式:通过阻塞队列实现生产者线程和消费者线程之间的数据传递,避免空耗资源。Java并发包中的BlockingQueue接口提供了这种功能。 2. 单例模式:在多线程环境下,确保类只有一个实例,通常采用双重...

    Java多线程编程实战指南核新篇&设计篇&以及和核新篇的案例代码

    这部分可能会涵盖设计模式,比如生产者消费者模式、单例模式(尤其是线程安全的单例)、读写锁模式等,这些都是在并发环境中常见的设计模式。同时,还会讲解如何避免并发问题,如死锁、活锁、饥饿等,并提供解决这些...

    c# 多线程 上位机

    3.3 队列操作:生产者线程向队列中添加消息,消费者线程从队列中取出并处理消息。利用`Add`和`Take`方法实现这一过程。 3.4 消息队列的优势:解耦合、异步处理、容错性、负载均衡等。 四、InterCup相关 虽然...

    线程安全队列Queue

    这意味着当队列满时,生产者线程将被阻塞直到队列有足够的空间;同样,当队列为空时,消费者线程将被阻塞直到队列中有新的元素加入。这种机制使得线程之间可以更安全、更高效地交换数据。 ##### 4.2 常用方法 `...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    - **生产者消费者模式**:使用`BlockingQueue`实现线程间的协同工作。 - **单例模式**:考虑多线程环境下的线程安全单例实现,如双重检查锁定。 - **读写锁**:`ReentrantReadWriteLock`提供读写分离的锁机制,...

    JAVAJAVA多线程教学演示系统论文

    系统可能包含了多种线程模型的示例,如生产者消费者模型、读写锁模型等,通过图形化界面展示线程间的交互,帮助学生直观地理解多线程的运行机制。 6. **系统性能与优化**:论文可能还分析了系统的性能特性,包括...

    秒杀多线程 第1-4篇合集

    在多线程编程中,避免不了遇到各种经典同步问题,如生产者消费者问题、读者写者问题等。这些问题的解决需要综合使用各种同步机制,同时还要掌握相应的“内功心法”,即深入理解多线程同步互斥的原理和应用。 四、多...

    Java多线程编程实战指南 设计模式篇

    6. 生产者-消费者模式:通过阻塞队列实现线程间的协作,如BlockingQueue接口,避免资源浪费。 四、线程安全的数据结构 Java提供了一些线程安全的数据结构,如ConcurrentHashMap、CopyOnWriteArrayList等,它们在...

    Java线程讲解Java线程讲解

    4. **生产者消费者模式**:这是一个经典的多线程问题,通过生产者(Producer)和消费者(Consumer)模型来解决资源的生产和消费问题。 - **生产者**:负责生产数据并将其放入缓冲区。 - **消费者**:负责从缓冲区...

    JAVA 多线程学习笔记

    2. `BlockingQueue`:队列实现的线程通信,如`ArrayBlockingQueue`、`LinkedBlockingQueue`,适合生产者消费者模式。 五、线程池 1. `ExecutorService`:Java 5引入的线程池接口,通过`Executors`工厂类创建不同...

    java多线程设计模式

    - **生产者消费者模式**:使用`BlockingQueue`来协调生产者和消费者线程,避免资源浪费。 - **线程池模式**:通过`ExecutorService`和`ThreadPoolExecutor`管理线程,提高系统资源利用率,避免频繁创建销毁线程。 ...

    Java多线程programmingShiZhanZhiNan(呵心篇).源码

    - **BlockingQueue**:如`ArrayBlockingQueue`,线程安全的数据结构,用于线程间的生产者-消费者模式。 4. **线程池**: - **ExecutorService与ThreadPoolExecutor**:Java提供的线程池接口和实现,可以有效管理...

    Java线程编程学习笔记(二)

    这些示例可能涵盖了各种场景,如生产者消费者模型、线程间的协作(通过信号量Semaphore)、线程优先级、守护线程(daemon threads)以及线程中断的处理。通过分析这些示例,我们可以更好地理解Java线程在实际开发中...

    深入Java多线程和并发编程

    - **阻塞队列(BlockingQueue)**:提供了一种线程安全的方式来处理生产者-消费者模式中的数据交换。 - **可重入锁(ReentrantLock)**:提供了比内置锁更灵活的锁定机制,支持公平性和非公平性锁定策略。 - **同步...

Global site tag (gtag.js) - Google Analytics