【Java进阶之多线程进阶】代码库(二)

生产者消费者.java

import java.util.concurrent.LinkedBlockingQueue;

public class LocalTest {
    public static void main(String[] args) {
        LinkedBlockingQueue<Object> list = new LinkedBlockingQueue<Object>();
        Storage store = new Storage(list);

        Producer p1 = new Producer(store);
        Producer p2 = new Producer(store);
        Producer p3 = new Producer(store);
        Producer p4 = new Producer(store);
        Producer p5 = new Producer(store);
        Producer p6 = new Producer(store);
        Producer p7 = new Producer(store);
        Producer p8 = new Producer(store);
        Producer p9 = new Producer(store);
        Producer p10 = new Producer(store);
        Producer p11 = new Producer(store);

        Consumer c1 = new Consumer(store);
        Consumer c2 = new Consumer(store);
        Consumer c3 = new Consumer(store);
        Consumer c4 = new Consumer(store);
        Consumer c5 = new Consumer(store);
        Consumer c6 = new Consumer(store);
        Consumer c7 = new Consumer(store);
        Consumer c8 = new Consumer(store);

        p1.start();
        p2.start();
        c2.start();
        p3.start();
        c1.start();
        p4.start();
        c3.start();
        p5.start();
        p6.start();
        c4.start();
        p7.start();
        p8.start();
        p9.start();
        c5.start();
        c6.start();
        p10.start();
        p11.start();
        c7.start();
        c8.start();
    }
}



import java.util.concurrent.LinkedBlockingQueue;

public class Storage {
    private LinkedBlockingQueue<Object> list;

    public Storage(LinkedBlockingQueue<Object> list) {
        this.list = list;
    }

    public LinkedBlockingQueue<Object> getList() {
        return list;
    }
}



import java.util.concurrent.LinkedBlockingQueue;

public class Producer extends Thread {
    private Storage str;

    public Producer(Storage str) {
        this.str = str;
    }

    public void run() {
        LinkedBlockingQueue<Object> list = str.getList();

        if (list.size() == 100) {
            System.out.println(Thread.currentThread().getName() + "-" + "仓库已满!");
        }

        for (int i=1; i<Math.random()*20; i++) {
            try {
                list.put(new Object());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "-" + "库存:" + list.size());
    }
}



import java.util.concurrent.LinkedBlockingQueue;

public class Consumer extends Thread {
    private Storage str;

    public Consumer(Storage str) {
        this.str = str;
    }

    public void run() {
        LinkedBlockingQueue<Object> list = str.getList();

        if (list.size() == 100) {
            System.out.println(Thread.currentThread().getName() + "-" + "仓库已满!");
        }

        for (int i=1; i<5; i++) {
            try {
                list.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "-" + "库存:" + list.size());
    }
}

同步方法.java

/**
 * 同步方法: 有 synchronized 关键字修饰的方法, 当方法调用 synchronized 关键字声明的时候, 
 * Java 内置锁会保护整个方法, 即在调用该方法之前, 需要获得内置锁, 否则就会处于阻塞状态. 
 * 注意: synchronized 关键字亦可以修饰静态方法, 如若调用此方法将会锁住整个类!!!
 */

线程局部变量.java

/**
 * Java 提供了很多种方式和工具类来帮助程序员简化多线程的开发, 同步方法是最简单和最常用的的一种!!!
 * 使用 ThreadLocal 类管理线程的局部变量, 使每个线程都能获得相互独立的副本, 每个线程都可以随意的修改自己的变量值而不会对其他线程产生任何影响. 
 */

public class Bank {
    //ThreadLocal 通常是 private static 类型
    private static ThreadLocal<Integer> account = new ThreadLocal<Integer>() {

        //重写 initialValue 方法, 将 account 的初始值设置为 100
        @Override
        protected Integer initialValue() {
            return 100;
        }
    };

    public void deposit(int money) {
        account.set(account.get() + money);
    }

    public int getAccount() {
        return account.get();
    }
}


public class Transfer implements Runnable {
    private Bank bank;

    public Transfer(Bank bank) {
        this.bank = bank;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            bank.deposit(10); //每次存 10 块
            System.out.println("账户余额是: " + bank.getAccount());
        }
    }
}
//ThreadLocal 和同步机制都是为了解决多线程中相同变量的访问冲突问题, 前者采用了"空间换时间"的方式, 后者采用了"时间换空间"的方式.

信号量.java

/**
 * 信号量是由 Dijkstra 在 1968 年发明的, 该概念最初是用于在进程间发信号的一个整数值.
 *     一个信号量有且仅有 3 种操作, 且全部是原子的: 1.初始化, 2.增加, 3.减少
 *        增加可以为一个进程解除阻塞;
 *        减少可以让一个进程进入阻塞;
 *
 * Java 的 Semaphore 类是一个计数信号量, 从概念上讲, 信号量维护了一个许可集, 如有必要. 在许可可用之前会阻塞每一个 
 * acquire(), 然后再获取该许可, 每个 release() 添加一个许可, 从而可能释放一个正在阻塞的获取者. 但是, 不使用实际的
 * 许可对象, Semaphore 只对可用许可的号码进行计数, 并采取相应的行动. 声明如下:
 *     public Semaphore(int permits, boolean fair) 参数说明:
 *          permits: 初始的可用许可数目, 该值可能为负数, 在这种情况下, 必须在授予任何获取前进行释放.
 *          fair: 如果该信号量保证在争用时按先进先出的授权许可, 则为 true, 反之为 false.
 *     1.为了从信号量获得一个许可, 需要使用 acquire() 方法, 声明如下:
 *          public void acquire() throws InterruptedException
 *     2.为了释放一个许可到的信号量, 需要使用 release() 方法, 声明如下:
 *          public void release()
 */

public class Bank {
    private int account = 100;

    public void deposit(int money) {
        account += money; 
    }

    public int getAccount() {
        return account;
    }
}

public class Transfer implements Runnable {
    private Bank bank;
    private Semaphore semaphore;

    public Transfer(Bank bank, Semaphore semaphore) {
        this.bank = bank;
        this.semaphore = semaphore;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                semaphore.acquire(); //获得许可, 进入临界区
                bank.deposit(10);
                System.out.println("账户的余额是: " + bank.getAccount());
                semaphore.release(); //释放, 离开临界区
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//总结: Java 中的 Semaphore 类是一个计数信号量, 而且必须由获取的线程来释放, 这对信号量的管理会造成困难.

原子变量.java

/**
 * 需要使用线程同步的根本原因在于对普通变量的操作并不是原子的! 所谓原子操作是将读取变量值、修改变量值、保存变量值看作一个整体,要么同时完成,要么不同时完成,参考数据库的事务原理。
 */

public class Bank {
    private AtomicInteger account = new AtomicInteger(100);

    //存钱
    public void deposit(int money) {
        account.addAndGet(money); //以原子方式将给定值和当前值相加
    }

    //取钱
    public int getAccount() {
        return account.get();
    }
}

public class Transfer implements Runnable {
    private Bank bank;

    public Transfer(Bank bank) {
        this.bank = bank;
    }

    public void run() {
        for ( int i = 0; i < 10; i++ ) {
            bank.deposit(10);
            System.out.println("账户的余额是: " + bank.getAccount());
        }
    }
}

哲学家就餐.java

/**
 * 死锁: 哲学家就餐问题!
 *
 * 布尔变量: true 表示吃饭! false 表示思考!
 */

private boolean state;

public synchronized void eating() {
    if (!state) {
        if (chopstickArray.getRight(id).isAvailable()) { //右手边筷子可用
            if (chopstickArray.getLeft(id).isAvailable()) { //左手边筷子可用

                //设置筷子不可用
                chopstickArray.getRight(id).setAvailable(false);
                chopstickArray.getleft(id).setAvailable(false);

                try {
                    Thread.sleep(1000); //设置吃饭时间为 1 秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    wait(new Random().nextInt(100)); //小于 0.1 秒时间内检查筷子是否可用
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } 
        } else {
            try {
                wait(new Random().nextInt(100)); //小于 0.1 秒时间内检查筷子是否可用
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    state = true;
}
//总结: 本例难在如何解决死锁问题, 即如果 5 个哲学家都在等待别人的筷子, 程序就会进入死锁状态!
//当遇到比较复杂的问题时, 通常利用面向对象的思想将问题分割.
//如: 本例可以分割成筷子和哲学家两种对象, 每个对象只关心自己的状态, 不考虑与其他对象的交互, 问题就会变得简单明了!!

重入锁.java

/**
 * ReentrantLock 类是可重入、互斥实现了 Lock 接口的锁, 具有和使用 Synchronized 方法相同的基本行为和语义, 并且大大扩展了其能力.
 */

public class Bank {
    private int account = 100;
    private Lock lock = new ReentrantLock();

    public void deposit(int money) {
        lock.lock(); //锁
        try {
            account += money; 
        } finally { 
            lock.unlock(); //解锁
        }
    }

    public int getAccount() {
        return account;
    }
}


public class Transfer implements Runnable {
    private Bank bank;

    public Transfer(Bank bank) {
        this.bank = bank;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            bank.deposit(10); //每次存 10 块钱
            System.out.println("账户余额是: " + bank.getAccount());
        }
    }
}

阻塞队列.java

/**
 * 多线程基础实例介绍了如何在底层实现线程同步, 但在实际开发中应尽量远离底层结构, 使用 java.util.concurrent 包将有助于简化开发.
 */

public class Producer implements Runnable {
    public void run() { 
        for (int i = 0; i<size; i++) { //size 为成员变量, 表示添加商品的次数
            int b = new Random().nextInt(255);

            System.out.println("生产商品: " + b);

            try {
                queue.put(b); //向队列中添加元素
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("仓库中还有" + queue.size() + "个商品");

            try {
                Thread.sleep(1000); //休眠 1 秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//BlockingQueue<E> 接口的使用: 定义了阻塞队列的常用方法, 如: 对于添加元素有 add(), offer(), put() 这三种方法.当队列满时, add 方法会抛出异常, offer 方法会返回 false, put 方法会产生阻塞!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值