最近项目中提了资源共享的需求,所以就在网上看了一些多线程的例子,觉得有一个博主的文章比较不错。里面有多线程同步锁的代码详解,开启了我对代码安全更深层次的认识,代码简单易懂。还有一个死锁的demo,现在经过修改后贴在自己园里,供理解其思想。
举一个线程作用的例子: 一个Java虚拟机的实例运行在一个单独的进程中,不同的线程共享Java虚拟机进程所属的堆内存。这也是为什么不同的线程可以访问同一个对象。线程彼此共享堆内存并保有他们自己独自的栈空间。这也是为什么当一个线程调用一个方法时,他的局部变量可以保证线程安全。但堆内存并不是线程安全的,必须通过显示的声明同步来确保线程安全。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
1 public class Account { 2 private String accountNo; 3 private double balance; 4 public Account(String accountNo,double balance){ 5 this.accountNo=accountNo; 6 this.balance=balance; 7 } 8 9 public double getBalance() { 10 return balance; 11 } 12 13 public void setBalance(double balance) { 14 this.balance = balance; 15 } 16 17 public String getAccountNo() { 18 return accountNo; 19 } 20 21 public void setAccountNo(String accountNo) { 22 this.accountNo = accountNo; 23 } 24 25 @Override 26 public boolean equals(Object o) { 27 if (this == o) return true; 28 if (o == null || getClass() != o.getClass()) return false; 29 30 Account account = (Account) o; 31 32 return accountNo.equals(account.accountNo); 33 34 } 35 36 @Override 37 public int hashCode() { 38 return accountNo.hashCode(); 39 } 40 }
DrawThread.java
1 public class DrawThread extends Thread { 2 private Account account; 3 private double drawAmount; 4 5 public DrawThread(String name, Account account, double drawAmount) { 6 super(name); 7 this.account = account; 8 this.drawAmount = drawAmount; 9 } 10 public void run(){ 11 synchronized (account){ 12 if(account.getBalance()>=drawAmount){ 13 System.out.println(getName() + "取钱成功,吐出钞票: " + drawAmount); 14 try{ 15 Thread.sleep(1); 16 }catch(InterruptedException ex){ 17 ex.getStackTrace(); 18 } 19 account.setBalance(account.getBalance()-drawAmount); 20 System.out.println(getName()+"\t余额为:"+account.getBalance()); 21 }else{ 22 System.out.println(getName()+"取钱失败,余额不足"); 23 } 24 } 25 } 26 }
DrawTest.java
1 public class DrawTest { 2 public static void main(String[] args){ 3 Account acct=new Account("1234567",1000); 4 new DrawThread("甲",acct,800).start(); 5 new DrawThread("乙",acct,800).start(); 6 } 7 }
1 public class DrawThread extends Thread { 2 private Account account; 3 private double drawAmount; 4 5 public DrawThread(String name, Account account, double drawAmount) { 6 super(name); 7 this.account = account; 8 this.drawAmount = drawAmount; 9 } 10 public void run(){ 11 // synchronized (account){ 12 if(account.getBalance()>=drawAmount){ 13 System.out.println(getName() + "取钱成功,吐出钞票: " + drawAmount); 14 try{ 15 Thread.sleep(1); 16 }catch(InterruptedException ex){ 17 ex.getStackTrace(); 18 } 19 account.setBalance(account.getBalance()-drawAmount); 20 System.out.println("\t余额为:"+account.getBalance()); 21 }else{ 22 System.out.println(getName()+"取钱失败,余额不足"); 23 } 24 // } 25 } 26 }
会出现这些情况的结果:
1 public class Account { 2 private String accountNo; 3 private double balance; 4 public Account(String accountNo,double balance){ 5 this.accountNo=accountNo; 6 this.balance=balance; 7 } 8 9 //因为账户余额不可以随便更改,所以只为balance提供getter方法 10 public double getBalance() { 11 return balance; 12 } 13 14 public String getAccountNo() { 15 return accountNo; 16 } 17 18 public void setAccountNo(String accountNo) { 19 this.accountNo = accountNo; 20 } 21 22 @Override 23 public boolean equals(Object o) { 24 if (this == o) return true; 25 if (o == null || getClass() != o.getClass()) return false; 26 27 Account account = (Account) o; 28 29 return accountNo.equals(account.accountNo); 30 31 } 32 33 @Override 34 public int hashCode() { 35 return accountNo.hashCode(); 36 } 37 38 //提供一个线程安全的draw()方法来完成取钱操作 39 public synchronized void draw(double drawAmount){ 40 if(balance>=drawAmount){ 41 System.out.println(Thread.currentThread().getName()+"取钱成功!吐出钞票:"+drawAmount); 42 try{ 43 Thread.sleep(1); 44 }catch (InterruptedException ex){ 45 ex.printStackTrace(); 46 } 47 balance-=drawAmount; 48 System.out.println("\t余额为:"+balance); 49 }else{ 50 System.out.println(Thread.currentThread().getName()+"取钱失败,余额不足"); 51 } 52 } 53 }
DrawThread.java
1 public class DrawThread extends Thread { 2 private Account account; 3 private double drawAmount; 4 5 public DrawThread(String name, Account account, double drawAmount) { 6 super(name); 7 this.account = account; 8 this.drawAmount = drawAmount; 9 } 10 public void run(){ 11 account.draw(drawAmount); 12 } 13 }
DrawTest.java
1 public class DrawTest { 2 public static void main(String[] args){ 3 Account acct=new Account("1234567",1000); 4 new DrawThread("甲",acct,800).start(); 5 new DrawThread("乙",acct,800).start(); 6 } 7 }
1 class X{ 2 //定义锁对象 3 private final ReentrantLock lock=new ReentrantLock(); 4 //定义需要保证线程安全的方法 5 public void m(){ 6 //加锁 7 lock.lock(); 8 try{ 9 //...method body 10 } 11 //使用finally块来保证释放锁 12 finally{ 13 lock.unlock(); 14 } 15 } 16 }
将Account.java修改为:
1 public class Account { 2 private final ReentrantLock lock=new ReentrantLock(); 3 private String accountNo; 4 private double balance; 5 public Account(String accountNo,double balance){ 6 this.accountNo=accountNo; 7 this.balance=balance; 8 } 9 10 //因为账户余额不可以随便更改,所以只为balance提供getter方法 11 public double getBalance() { 12 return balance; 13 } 14 15 public String getAccountNo() { 16 return accountNo; 17 } 18 19 public void setAccountNo(String accountNo) { 20 this.accountNo = accountNo; 21 } 22 23 @Override 24 public boolean equals(Object o) { 25 if (this == o) return true; 26 if (o == null || getClass() != o.getClass()) return false; 27 28 Account account = (Account) o; 29 30 return accountNo.equals(account.accountNo); 31 32 } 33 34 @Override 35 public int hashCode() { 36 return accountNo.hashCode(); 37 } 38 39 //提供一个线程安全的draw()方法来完成取钱操作 40 public void draw(double drawAmount){ 41 //加锁 42 lock.lock(); 43 try { 44 if (balance >= drawAmount) { 45 System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawAmount); 46 try { 47 Thread.sleep(1); 48 } catch (InterruptedException ex) { 49 ex.printStackTrace(); 50 } 51 balance -= drawAmount; 52 System.out.println("\t余额为:" + balance); 53 } else { 54 System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足"); 55 } 56 }finally { 57 lock.unlock(); 58 } 59 } 60 }
1 class A{ 2 public synchronized void foo(B b){ 3 System.out.println("当前线程名为:"+Thread.currentThread().getName()+"进入了A实例的foo()方法"); 4 try{ 5 Thread.sleep(200); 6 }catch(InterruptedException ex){ 7 ex.printStackTrace(); 8 } 9 System.out.println("当前线程名为:"+Thread.currentThread().getName()+"试图调用B实例的last()方法"); 10 b.last(); 11 } 12 public synchronized void last(){ 13 System.out.println("进入了A类的last()方法内部"); 14 } 15 } 16 class B{ 17 public synchronized void bar(A a){ 18 System.out.println("当前线程名为:"+Thread.currentThread().getName()+"进入了B实例的bar()方法"); 19 try{ 20 Thread.sleep(200); 21 }catch(InterruptedException ex){ 22 ex.printStackTrace(); 23 } 24 System.out.println("当前线程名为:"+Thread.currentThread().getName()+"试图调用A实例的last()方法"); 25 a.last(); 26 } 27 public synchronized void last(){ 28 System.out.println("进入了B类的last()方法内部"); 29 } 30 } 31 public class DeadLock implements Runnable{ 32 A a =new A(); 33 B b=new B(); 34 public void init(){ 35 Thread.currentThread().setName("主线程"); 36 a.foo(b); 37 System.out.println("进入了主线程之后..."); 38 } 39 public void run(){ 40 Thread.currentThread().setName("副线程"); 41 b.bar(a); 42 System.out.println("进入了副线程之后..."); 43 } 44 public static void main(String[] args){ 45 DeadLock d1=new DeadLock(); 46 new Thread(d1).start(); 47 d1.init(); 48 } 49 }
结果: