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());
}
}
}
}