美文网首页
2018-06-12 第三十七天

2018-06-12 第三十七天

作者: fe0180bd6eaf | 来源:发表于2018-06-12 22:36 被阅读0次

    一、线程安全

    线程安全的问题,是针对多线程的程序。单线程的情况下,是不存在线程安全问题。

    产生线程安全问题的原因:多个线程同时访问同一块代码。但是实际上我们希望该代码块是原子性的,在某一个时间点,只希望一个线程单独访问,不希望多个线程同时访问。

    解决方案

    1:同步代码块。

    synchronized (this) {

    //被同步的代码块;

    }

    synchronized(同步监视器对象) :java 关键字

    {}:同步代码块:希望在某一个时间点只有一个线程访问的代码块。

    执行过程:

    1:线程1 执行到了同步代码块。要对同步监视器进行检测,看是否被其他的线程上锁了。发现没有上锁,那么线程1对同步监视器对象 上锁,并开始访问 同步代码块。

    2:线程2 执行到了同步代码块,检测同步监视器,发现监视器已经被 线程1 上锁了。就进入就绪状态,等待cpu 下次调度执行。

    3:线程1 执行完毕同步代码块,然后对 同步监视器对象  解锁。并执行后续的代码。

    4:当 线程2 被再次调度执行的时候,发现同步监视器对象已经被解锁,那么就对同步监视器对象 加锁 并访问 同步代码块。

    类似于上厕所:没人(有人就等着),进去,锁门,方便,然后冲水开门出去。下一个。

    2:同步方法:

    相当于把整个方法体都同步了。

    同步监视器对象是this(实例方法)。

    同步方法,会导致在任意时刻,只能有一个线程访问该方法。一个线程执行完毕方法之后,其他的线程才能访问。

    同步块和同步方法都会导致程序的效率降低。多了检测监视器对象是否加锁,加锁和解锁的过程。

    3:如果同步方法是静态方法,那么同步监视器对象是当前类的大 Class 对象。

     

    import java.util.concurrent.ConcurrentHashMap;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    /**

     * 模拟两个人取钱,说明线程安全的问题。

     * 张三和 张三媳妇,一起去取钱。

     * 一个在ATM

     * 一个在柜台。

     * 一共1500

     * 每个人都想取1000块钱。

     *

     */

    public class AccountTest {

    public static void main(String[] args) {

    PersonRunnable runnable = new PersonRunnable();

    Thread zhangSan = new Thread(runnable, "张三");

    Thread zhangSanXiFu = new Thread(runnable, "张三媳妇");

    zhangSan.start();

    zhangSanXiFu.start();

    }

    }

    /**

     * 账户类

     *

     */

    class Account{

    private int money = 1500;

    //第三种线程同步的实现 jdk1.5出现的。

    private Lock lock = new ReentrantLock();

    /**

     * 取现

     * @param money  取钱的钱数

     * @return 如果取钱成功,返回 true ,否则返回 false。

     */

    public /*synchronized*/ boolean withDrawMoney(int money) {

    //同步代码块

    //synchronized (this) {

    //在try 外面将 需要同步的代码锁住,然后将需要同步的代码,放到try 块中。

    lock.lock();

    try {

    if(this.money >= money){

    try {

    Thread.sleep(10);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    this.money -= money;

    System.out.println(Thread.currentThread().getName() + "--> 取钱[成功]。余额为:"+this.money);

    return true;

    }

    }finally{

    //必须执行

    lock.unlock();

    }

    // }

    System.out.println(Thread.currentThread().getName() + "--> 取钱[失败]。余额为:"+this.money);

    return false;

    }

    }

    class PersonRunnable implements Runnable{

    //唯一的账户

    private Account account = new Account();

    public void run() {

    account.withDrawMoney(1000);

    }

    }

    二、关于监视器的选择

    如果想实现线程间的互斥访问同步代码块,那么监视器对象必须唯一。只有一个实例存在。

    1:this:保证只有一个this。这个对象只被实例化了一次。

    2:final static Object obj = new Object();

    3: 当一个类加载的时候,类加载只有一次,会产生2部分内容。1部分是类的字节码元数据(方法区)。还会生成一个对象(堆区)。对象的类型是  描述类的类型  Class。

    这个大Class 类型的对象也是唯一的。可以通过  类名.class  来得到这个唯一的描述当前类的对象。

    好处:不可变以及唯一性。

    当前类的大Class 对象。

    /**

     * 继承线程Thread 类,来模拟火车站售票。

     * 每一个售票窗口都代表了一个线程。

     * 所有的售票员,都访问同一个售票系统。

     *

     */

    public class TestTicket {

    public static void main(String[] args) {

    TicketSystem system = new TicketSystem();

    Saler saler1 = new Saler(system);

    saler1.setName("售票员小姐姐-1");//设置线程的名字

    Saler saler2 = new Saler(system);

    saler2.setName("售票员小姐姐-2");//设置线程的名字

    Saler saler3 = new Saler(system);

    saler3.setName("售票员小姐姐-3");//设置线程的名字

    Saler saler4 = new Saler(system);

    saler4.setName("售票员小姐姐-4");//设置线程的名字

    Saler saler5 = new Saler(system);

    saler5.setName("售票员小姐姐-5");//设置线程的名字

    saler1.start();

    saler2.start();

    saler3.start();

    saler4.start();

    saler5.start();

    }

    }

    //售票系统

    class TicketSystem{

    //剩余票数

    private int count = 100;

    // static Object o = new Object();

    /**

     * @param count 本次卖票的张数

     * @return 是否售票成功

     */

    public /*synchronized*/ boolean sale(int count){

    //使用当前类的 大 Class 对象 作为 监视器。具有不可变和唯一的特性。

    synchronized (TicketSystem.class) {

    if(this.count >= count){

    this.count -= count;

    System.out.println(Thread.currentThread().getName() + " 卖出了 "+count+" 张票,剩余票数为:"+this.count);

    //让当前线程等待一下下 100

    try {

    Thread.sleep(10);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    return true;

    }

    }

    //

    System.out.println(Thread.currentThread().getName() + " 售票失败 ,余票不足 ,剩余票数为:"+this.count);

    return false;

    }

    }

    //售票员

    class Saler extends Thread{

    //售票员需要访问唯一的票务系统

    //多个线程对象,访问同一个对象的数据,可以使用同时持有唯一对象引用的方法。

    private TicketSystem system;

    public Saler(TicketSystem system) {

    this.system = system;

    }

    //线程任务的主体部分。

    public void run() {

    boolean result = system.sale(1);

    while(result){

    result = system.sale(1);

    }

    }

    }

    三、单例模式最终版

    public class SingleTonTest {

    static int num = 0;

    public static void main(String[] args) {

    //在线程1 中 得到唯一的实例。

    new Thread(){

    @Override

    public void run() {

    MySingleton singleton1 = MySingleton.getInstance();

    System.out.println(singleton1);

    }

    }.start();

    //在线程2中再得到一次

    new Thread(){

    @Override

    public void run() {

    MySingleton singleton2 = MySingleton.getInstance();

    System.out.println(singleton2);

    }

    }.start();

    //使用Runnable 接口,匿名内部类

    new Thread(new Runnable() {

    @Override

    public void run() {

    }

    }).start();

    //面试题

    new Thread(new Runnable() {// 11111

    @Override

    public void run() {

    System.out.println(22222);

    }

    }){

    public void run() {

    System.out.println(11111);

    };

    }.start();

    }

    }

    //懒汉模式单例类

    class MySingleton{

    private int num = 10;

    private static MySingleton singleton;

    private MySingleton(){}

    //对外的提供的唯一的 方位 唯一实例的方法

    public /*synchronized*/ static MySingleton getInstance(){

    //为了提高效率,后续的该方法的访问都不会再进行 加锁和解锁的 和监视器是否有锁的判断

    if(singleton == null){

    synchronized (MySingleton.class) {

    //为了保证唯一的实例

    if(singleton == null){

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    singleton = new MySingleton();

    }

    }

    }

    return singleton;

    }

    public void setNum(int num){

    this.num = num;

    }

    public int getNum(){

    return num;

    }

    }

    四、线程死锁

    线程死锁:DeadLock。两个进程相互上锁,当两个进程同时被锁住的时候,程序将无法继续向下执行,此时即为死锁。

    static Object o1 = new Object()

    static Object o2 = new Object();

    //线程-1 执行的代码

    synchronized(o1){

    // thread-1 stop here

    synchronized(o2){

    }

    }

    //线程-2 执行 代码

    synchronized(o2){

    // thread-2 stop here

    synchronized(o1){

    }

    }

    出现死锁的条件:

    1:同步代码块嵌套。

    2:多个线程之间已经锁住的资源为彼此请求锁住的资源。

    如果死锁已经产生,那么没有办法解决,只能在编码的时候避免出现死锁。

    避免死锁:避免出现嵌套的同步代码块。

    public class DeadLockTest {

    public static void main(String[] args) {

    new DeadLockThread(0).start();

    new DeadLockThread(1).start();

    }

    }

    class DeadLockThread extends Thread{

    //两个监视器对象

    final static Object O1 = new Object();

    final static Object O2 = new Object();

    private int id;

    public DeadLockThread(int id) {

    this.id = id;

    }

    @Override

    public void run() {

    if(id == 0){

    synchronized (O1) {

    System.out.println(Thread.currentThread().getName()+"--O1-->O2");

    try {

    Thread.sleep(100);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    synchronized (O2) {

    System.out.println(Thread.currentThread().getName()+"--O1-->O2--------");

    }

    }

    }else{

    synchronized (O2) {

    System.out.println(Thread.currentThread().getName()+"--O2-->O1");

    synchronized (O1) {

    System.out.println(Thread.currentThread().getName()+"--O2-->O1--------");

    }

    }

    }

    }

    }

    五、线程间通信-管道流

    使用管道流实现多个线程之间的信息的交互。

    import java.io.BufferedReader;

    import java.io.BufferedWriter;

    import java.io.IOException;

    import java.io.InputStreamReader;

    import java.io.OutputStreamWriter;

    import java.io.PipedInputStream;

    import java.io.PipedOutputStream;

    /**

     * 两个线程 独立的 类,进行信息的交互

     * 管道输出流写出的数据,被管道输入流读取到。

     * 在一个线程中用管道输出流写出数据。

     * 在另外一个线程中使用管道输入流读取 管道输出流写出的数据

     *

     */

    public class PipedStreamTest {

    public static void main(String[] args) throws Exception {

    PipedInputStream pis = new PipedInputStream();

    PipedOutputStream pos = new PipedOutputStream();

    //可以通过构造方法将管道输入输出流建立关联,也可以通过方法。

    pis.connect(pos);

    PipedOutputStreamThread thread1 = new PipedOutputStreamThread(pos);

    PipedInputStreamThread thread2 = new PipedInputStreamThread(pis);

    new Thread(){

    public void run() {

    thread2.start();

    };

    }.start();

    Thread.sleep(5000);

    new Thread(){

    public void run() {

    thread1.start();

    };

    }.start();

    }

    }

    //管道输出流的线程类

    class PipedOutputStreamThread extends Thread{

    //持有管道输出流的引用。

    private PipedOutputStream pos;

    public PipedOutputStreamThread(PipedOutputStream pos) {

    this.pos = pos;

    }

    public void run() {

    try {

    //使用管道输出流写出数据

    String str = "多年不见,你还好么?";

    // byte[] bs = str.getBytes();

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(pos));

    bw.write(str);

    // pos.write(bs);

    bw.newLine();

    bw.flush();

    bw.close();

    } catch (Exception e) {

    e.printStackTrace();

    }finally {

    try {

    pos.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    }

    }

    //用于读取管道输出流写出数据的输入流 

    class PipedInputStreamThread extends Thread{

    private PipedInputStream pis;

    public PipedInputStreamThread(PipedInputStream pis) {

    this.pis = pis;

    }

    @Override

    public void run() {

    try {

    BufferedReader br = new BufferedReader(new InputStreamReader(pis));

    String str = br.readLine();

    System.out.println("接收到的内容为:"+str);

    br.close();

    } catch (Exception e) {

    e.printStackTrace();

    }finally{

    try {

    pis.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    }

    }

    六、生产者消费者典型案例

    都是Object类的方法。

    Object obj = new Object();

    obj .wait():让当前线程在当前对象上等待。 让执行这句代码的线程在obj上等待。

    obj .notify(): 将在obj 对象上等待的某个线程唤醒。

    obj .notifyAll(): 将在obj 对象上等待的所有的线程唤醒。

    这三个方法使用的环境:必须让当前线程持有当前对象监视器的锁。

    obj .wait()  表示 当前线程必须对 obj 上锁了。

    import java.util.Arrays;

    /**

     * 自定义容器  模拟栈

     * 底层使用数组来实现。Object

     *

     */

    public class MyStack {

    //定义栈的初始化容量

    public static final int INIT_CAPACITY = 1;

    //栈顶指针 所有对元素的操作都是 通过栈顶指针来完成的。

    private int index;

    //存放元素数组

    private Object[] elementData;

    public MyStack() {

    elementData = new Object[INIT_CAPACITY];

    index = 0;

    }

    /**

     * 将 e 压入栈顶

     * @param e

     */

    public void push(E e){

    synchronized (this) {

    if(isFull()){

    //如果栈已满

    //让生产者线程在  当前容器上等待。当前线程进入阻塞状态。并对当前对象解锁。

    try {

    this.wait();

    } catch (InterruptedException e1) {

    e1.printStackTrace();

    }

    }

    try {

    Thread.sleep(300);

    } catch (InterruptedException e1) {

    e1.printStackTrace();

    }

    //生产者顺利的生产了一个面包

    elementData[index++] = e;

    System.out.println(Thread.currentThread().getName()+"--生产了一个:"+e);

    //TODO  通知消费者你可以继续消费了

    //生产者线程通知消费者线程可以继续消费了,消费者线程如果处于阻塞状态,那么消费者线程从阻塞状态进入 就绪状态,等待cpu 的下次的调度执行。

    this.notify();

    }

    }

    /**

     * 弹栈操作

     * @return

     */

    public synchronized void pop() {

    if(isEmpty()){

    //消费者线程,发现容器空了,那么就在容器上等待。导致消费者线程进入阻塞状态,并释放对 this 的锁

    try {

    this.wait();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    try {

    Thread.sleep(300);

    } catch (InterruptedException e1) {

    e1.printStackTrace();

    }

    //先指针下移

    index --;

    E e = (E)elementData[index];

    elementData[index] = null;

    System.out.println(Thread.currentThread().getName() + "--消费了:"+e);

    //如果生产者线程此刻处于阻塞状态。那么会让生产者线程从阻塞状态进入就绪状态,等待cpu 的下次调度,如果被调度执行,那么生产者从上次等待的位置继续执行。

    //消费者线程通知在 this 上等待的生产者线程,可以继续生产了。

    this.notify();

    }

    /**

     * 获取栈顶元素,但是不删除

     * @return

     */

    public E peek()throws Exception{

    if(isEmpty()){

    Exception e = new Exception("囊中羞涩,别摸了!");

    throw e;

    }else{

    return (E)elementData[index-1];

    }

    }

    /**

     * 容器中是否包含 e

     * @param e

     * @return

     */

    public boolean search(E e){

    if(isEmpty())

    return false;

    //如果 e 是 null   判断 elementData 是否有null

    if(e == null){

    for(int i=0;i

    if(elementData[i] == null)

    return true;

    }

    }else{//e 不是null  

    for(int i=0;i

    if(e.equals(elementData[i]))

    return true;

    }

    }

    return false;

    }

    /**

     * 判断栈是否满了

     * @return

     */

    private boolean isFull(){

    return index == elementData.length;

    }

    /**

     * 栈是否为空

     * @return

     */

    public boolean isEmpty(){

    return index == 0;

    }

    /**

     * 得到元素的个数

     * @return

     */

    public int size(){

    return index;

    }

    /**

     * 容量

     * @return

     */

    public int capacity(){

    return elementData.length;

    }

    @Override

    public String toString() {

    return Arrays.toString(Arrays.copyOf(elementData, index));

    }

    }

    public class PCTest {

    public static void main(String[] args) {

    MyStack stack = new MyStack<>();

    Producer producer = new Producer(stack);

    producer.setName("巴莉甜甜");

    Consumer consumer = new Consumer(stack);

    consumer.setName("YiBao");

    producer.start();

    consumer.start();

    }

    }

    /**

     * 消费者线程 ,消费面包

     *

     */

    public class Consumer extends Thread{

    private MyStack stack;

    Consumer(MyStack stack) {

    super();

    this.stack = stack;

    }

    @Override

    public void run() {

    for(int i=0;i<10;i++){

    stack.pop();

    }

    }

    }

    /**

     * 面包类

     *

     */

    public class Bread {

    private int id;

    Bread(int id) {

    super();

    this.id = id;

    }

    @Override

    public String toString() {

    return "面包 [id=" + id + "]";

    }

    }

    /**

     * 生产者线程

     *

     */

    public class Producer extends Thread{

    private MyStack stack;

    Producer(MyStack stack) {

    super();

    this.stack = stack;

    }

    //生产十个面包

    public void run() {

    for(int i=0;i<10;i++){

    stack.push(new Bread(i));

    }

    }

    }

    七、多个消费者生产者出现的问题

    1:消费者1 开始消费,等待了。

    2:消费者2 开始消费,等待了。

    3:生产者 生产了一个馒头,唤醒了 消费者1. 生产者继续生产,等待了。

    4:消费者1 开始消费,结果把消费者2 唤醒了。消费者1 等待了。

    5:消费者2 开始消费,容器已经是空的了。 数组下标越界。

    解决:

    import java.util.Arrays;

    /**

     * 自定义容器  模拟栈

     * 底层使用数组来实现。Object

     *

     */

    public class MyStack {

    //定义栈的初始化容量

    public static final int INIT_CAPACITY = 1;

    //栈顶指针 所有对元素的操作都是 通过栈顶指针来完成的。

    private int index;

    //存放元素数组

    private Object[] elementData;

    public MyStack() {

    elementData = new Object[INIT_CAPACITY];

    index = 0;

    }

    //让生产者线程在o1上等待。 在o2上唤醒。

    static Object o1 = new Object();

    //让消费者线程在o2 上等待。在o1上唤醒。

    static Object o2 = new Object();

    /**

     * 将 e 压入栈顶

     * @param e

     */

    public void push(E e){

    synchronized (o1) {

    while(isFull()){//如果栈已满

    //让生产者线程在  当前容器上等待。当前线程进入阻塞状态。并对当前对象解锁。

    try {

    o1.wait();

    } catch (InterruptedException e1) {

    e1.printStackTrace();

    }

    }

    // try {

    // Thread.sleep(300);

    // } catch (InterruptedException e1) {

    // e1.printStackTrace();

    // }

    //生产者顺利的生产了一个面包

    elementData[index++] = e;

    System.out.println(Thread.currentThread().getName()+"--生产了一个:"+e);

    //TODO  通知消费者你可以继续消费了

    //生产者线程通知消费者线程可以继续消费了,消费者线程如果处于阻塞状态,那么消费者线程从阻塞状态进入 就绪状态,等待cpu 的下次的调度执行。

    // this.notifyAll();

    synchronized (o2) {

    o2.notify();

    }

    }

    }

    /**

     * 弹栈操作

     * @return

     */

    public /*synchronized */void pop() {

    synchronized (o2) {

    while(isEmpty()){

    //消费者线程,发现容器空了,那么就在容器上等待。导致消费者线程进入阻塞状态,并释放对 this 的锁

    try {

    o2.wait();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    // try {

    // Thread.sleep(300);

    // } catch (InterruptedException e1) {

    // e1.printStackTrace();

    // }

    //先指针下移

    index --;

    E e = (E)elementData[index];

    elementData[index] = null;

    System.out.println(Thread.currentThread().getName() + "--消费了:"+e);

    //如果生产者线程此刻处于阻塞状态。那么会让生产者线程从阻塞状态进入就绪状态,等待cpu 的下次调度,如果被调度执行,那么生产者从上次等待的位置继续执行。

    //消费者线程通知在 this 上等待的生产者线程,可以继续生产了。

    // this.notifyAll();

    synchronized (o1) {

    o1.notify();

    }

    }

    }

    /**

     * 获取栈顶元素,但是不删除

     * @return

     */

    public E peek()throws Exception{

    if(isEmpty()){

    Exception e = new Exception("囊中羞涩,别摸了!");

    throw e;

    }else{

    return (E)elementData[index-1];

    }

    }

    /**

     * 容器中是否包含 e

     * @param e

     * @return

     */

    public boolean search(E e){

    if(isEmpty())

    return false;

    //如果 e 是 null   判断 elementData 是否有null

    if(e == null){

    for(int i=0;i

    if(elementData[i] == null)

    return true;

    }

    }else{//e 不是null  

    for(int i=0;i

    if(e.equals(elementData[i]))

    return true;

    }

    }

    return false;

    }

    /**

     * 判断栈是否满了

     * @return

     */

    private boolean isFull(){

    return index == elementData.length;

    }

    /**

     * 栈是否为空

     * @return

     */

    public boolean isEmpty(){

    return index == 0;

    }

    /**

     * 得到元素的个数

     * @return

     */

    public int size(){

    return index;

    }

    /**

     * 容量

     * @return

     */

    public int capacity(){

    return elementData.length;

    }

    @Override

    public String toString() {

    return Arrays.toString(Arrays.copyOf(elementData, index));

    }

    }

    八、jdk1.5的针对同步嵌套的解决

    import java.util.Arrays;

    import java.util.concurrent.locks.Condition;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    /**

     * 自定义容器  模拟栈

     * 底层使用数组来实现。Object

     *

     */

    public class MyStack {

    //定义栈的初始化容量

    public static final int INIT_CAPACITY = 2;

    //栈顶指针 所有对元素的操作都是 通过栈顶指针来完成的。

    private int index;

    //存放元素数组

    private Object[] elementData;

    public MyStack() {

    elementData = new Object[INIT_CAPACITY];

    index = 0;

    }

    //使用jdk1.5 的针对线程死锁的解决方案。

    private Lock lock = new ReentrantLock();

    //需要两个Condition 对象。

    //生产者的Condition

    private Condition proCon = lock.newCondition();

    //消费者的Condition

    private Condition conCon = lock.newCondition();

    /**

     * 将 e 压入栈顶

     * @param e

     */

    public void push(E e){

    lock.lock();

    try {

    while(isFull()){//如果栈已满

    //让生产者线程在  当前容器上等待。当前线程进入阻塞状态。并对当前对象解锁。

    try {

    // o1.wait();

    proCon.await();

    } catch (InterruptedException e1) {

    e1.printStackTrace();

    }

    }

    //生产者顺利的生产了一个面包

    elementData[index++] = e;

    System.out.println(Thread.currentThread().getName()+"--生产了一个:"+e);

    //TODO  通知消费者你可以继续消费了

    //生产者线程通知消费者线程可以继续消费了,消费者线程如果处于阻塞状态,那么消费者线程从阻塞状态进入 就绪状态,等待cpu 的下次的调度执行。

    // this.notifyAll();

    // o2.notify();

    conCon.signal();

    } finally {

    lock.unlock();

    }

    }

    /**

     * 弹栈操作

     * @return

     */

    public /*synchronized */void pop() {

    lock.lock();

    try {

    while(isEmpty()){

    //消费者线程,发现容器空了,那么就在容器上等待。导致消费者线程进入阻塞状态,并释放对 this 的锁

    try {

    // o2.wait();

    conCon.await();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    //先指针下移

    index --;

    E e = (E)elementData[index];

    elementData[index] = null;

    System.out.println(Thread.currentThread().getName() + "--消费了:"+e);

    //如果生产者线程此刻处于阻塞状态。那么会让生产者线程从阻塞状态进入就绪状态,等待cpu 的下次调度,如果被调度执行,那么生产者从上次等待的位置继续执行。

    //消费者线程通知在 this 上等待的生产者线程,可以继续生产了。

    // this.notifyAll();

    // o1.notify();

    proCon.signal();

    } finally {

    lock.unlock();

    }

    }

    /**

     * 获取栈顶元素,但是不删除

     * @return

     */

    public E peek()throws Exception{

    if(isEmpty()){

    Exception e = new Exception("囊中羞涩,别摸了!");

    throw e;

    }else{

    return (E)elementData[index-1];

    }

    }

    /**

     * 容器中是否包含 e

     * @param e

     * @return

     */

    public boolean search(E e){

    if(isEmpty())

    return false;

    //如果 e 是 null   判断 elementData 是否有null

    if(e == null){

    for(int i=0;i

    if(elementData[i] == null)

    return true;

    }

    }else{//e 不是null  

    for(int i=0;i

    if(e.equals(elementData[i]))

    return true;

    }

    }

    return false;

    }

    /**

     * 判断栈是否满了

     * @return

     */

    private boolean isFull(){

    return index == elementData.length;

    }

    /**

     * 栈是否为空

     * @return

     */

    public boolean isEmpty(){

    return index == 0;

    }

    /**

     * 得到元素的个数

     * @return

     */

    public int size(){

    return index;

    }

    /**

     * 容量

     * @return

     */

    public int capacity(){

    return elementData.length;

    }

    @Override

    public String toString() {

    return Arrays.toString(Arrays.copyOf(elementData, index));

    }

    }

    相关文章

      网友评论

          本文标题:2018-06-12 第三十七天

          本文链接:https://www.haomeiwen.com/subject/kpqdeftx.html