线程通信synchronized、clock、Blockingqueue

    xiaoxiao2021-04-19  73

    1.synchronized修饰方法,提供线程安全的方法 共享变量作为共享类的私有变量

    // 构造一个同步监视类 public class Account { // 封装账户编号、账户余额两个成员变量 private String accountNo; private double balance; // 构造器 public Account(){} public Account(String accountNo , double balance) { this.accountNo = accountNo; this.balance = balance; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public String getAccountNo() { return this.accountNo; } // 因此账户余额不允许随便修改,所以只为balance提供getter方法, public double getBalance() { return this.balance; } // 提供一个线程安全draw()方法来完成取钱操作 // thread.sleep和thread.yield方法 不会释放同步监视器 public synchronized void draw(double drawAmount) { // 账户余额大于取钱数目 if (balance >= drawAmount) { System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawAmount); try { Thread.sleep(1); } catch (InterruptedException ex) { ex.printStackTrace(); } // 修改余额 balance -= drawAmount; System.out.println("\t余额为: " + balance); } else { System.out.println(Thread.currentThread().getName() + "取钱失败!余额不足!"); } } // 下面两个方法根据accountNo来重写hashCode()和equals()方法 public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if(this == obj) return true; if (obj !=null && obj.getClass() == Account.class) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } } public class DrawThread extends Thread { // 模拟用户账户 private Account account; // 当前取钱线程所希望取的钱数 private double drawAmount; public DrawThread(String name , Account account, double drawAmount) { super(name); this.account = account; this.drawAmount = drawAmount; } // 当多条线程修改同一个共享数据时,将涉及数据安全问题。 public void run() { // 提供一个线程安全draw()方法来完成取钱操作 // 同步方法的同步监视器是this,this代表调用draw()方法的对象。 // 也就是说:线程进入draw()方法之前,必须先对account对象的加锁。 account.draw(drawAmount); } public static void main(String[] args) { // 创建一个账户 Account acct = new Account("1234567" , 1000); // 模拟两个线程对同一个账户取钱 new DrawThread("甲" , acct , 800).start(); new DrawThread("乙" , acct , 800).start(); } //在account类中定义synchronized draw方法(而不是对于重写的run synchronized修饰)更符合面向对象的思想 // JDK 提供的stringbuilder单线程保证性能 ;stringbuffer多线程保证线程安全 // thread.sleep和thread.yield方法 不会释放同步监视器 }

    -

    2.clock思想类似synchronized 使用private final ReentrantLock lock = new ReentrantLock();

    import java.util.concurrent.locks.ReentrantLock; // 构造一个同步监视类 public class Account { // 定义锁对象,常用ReentrantLock 可重入锁 //ReentrantLock具有可重入性,不同线程都能再次加锁; reentrantlock对象由计数器追踪lock方法的嵌套调用 private final ReentrantLock lock = new ReentrantLock(); // 封装账户编号、账户余额的两个成员变量 private String accountNo; private double balance; public Account(){} public Account(String accountNo , double balance) { this.accountNo = accountNo; this.balance = balance; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public String getAccountNo() { return this.accountNo; } // 因此账户余额不允许随便修改,所以只为balance提供getter方法, public double getBalance() { return this.balance; } // 提供一个线程安全draw()方法来完成取钱操作 public void draw(double drawAmount) { // 加锁 lock.lock(); try { // 账户余额大于取钱数目 if (balance >= drawAmount) { System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawAmount); //正是这里的sleep 导致线程不安全必然发生,不然有可能不发生 try { Thread.sleep(1); } catch (InterruptedException ex) { ex.printStackTrace(); } // 修改余额 balance -= drawAmount; System.out.println("\t余额为: " + balance); } else { System.out.println(Thread.currentThread().getName() + "取钱失败!余额不足!"); } } //finally块 即使try不发生异常也会执行的代码块 finally { // 修改完成,释放锁 lock.unlock(); } } } public class DrawThread extends Thread { // 模拟用户账户 private Account account; // 当前取钱线程所希望取的钱数 private double drawAmount; public DrawThread(String name , Account account , double drawAmount) { super(name); this.account = account; this.drawAmount = drawAmount; } // 当多条线程修改同一个共享数据时,将涉及数据安全问题。 public void run() { account.draw(drawAmount); } public static void main(String[] args) { // 创建一个账户 Account acct = new Account("1234567" , 1000); new DrawThread("甲" , acct , 800).start(); new DrawThread("乙" , acct , 800).start(); } }

    -

    3.阻塞队列 BlockingQueue BlockingQueue bq = new ArrayBlockingQueue<>(1);

    // 引用内部类的方式 static方法需要一个外部类的对象引用 // 外部类的方法能够直接new // http://android.blog.51cto.com/268543/384844/ public class TheadBlockingQueue { public static void main(String[] args) { // 创建一个容量为1的BlockingQueue BlockingQueue<String> bq = new ArrayBlockingQueue<>(1); // 启动3条生产者线程 TheadBlockingQueue tq=new TheadBlockingQueue(); //注意这里内部类是如何引用的 tq.new Producer(bq).start(); tq.new Producer(bq).start(); tq.new Producer(bq).start(); // 启动一条消费者线程 tq.new Consumer(bq).start(); } class Producer extends Thread { private BlockingQueue<String> bq; public Producer(BlockingQueue<String> bq) { this.bq = bq; } public void run() { String[] strArr = new String[] { "Java", "Struts", "Spring" }; for (int i = 0 ; i < 9 ; i++ ) { //System.out.println(getName() + "生产者准备生产集合元素!"); try { Thread.sleep(100); // 尝试放入元素,如果队列已满,线程被阻塞 bq.put(strArr[i % 3]); } catch (Exception ex){ex.printStackTrace();} // 这里bq相当于bq.peek() 或者bq.element() System.out.println(getName() + "生产完成:" + bq); } } } class Consumer extends Thread { private BlockingQueue<String> bq; public Consumer(BlockingQueue<String> bq) { this.bq = bq; } public void run() { while(true) { // System.out.println(getName() + "消费者准备消费集合元素!"); try { Thread.sleep(200); // 尝试取出元素,如果队列已空,线程被阻塞 bq.take(); } catch (Exception ex){ex.printStackTrace();} System.out.println(getName() + "消费完成:" + bq.peek()); } } } }
    转载请注明原文地址: https://ju.6miu.com/read-675696.html

    最新回复(0)