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

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

实际编程遇到的困惑?
多个线程引用同一个实例对象,线程调用该对象的同一方法,需不需要排队,还是并发?
实验结果:并发
若对实例对象方法使用关键字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()类似,无法指定睡眠时间





你可能感兴趣的:(线程并发,消费者与生产者问题,JVM上锁机制)