1、线程同步
线程同步是为了防止多个线程访问一个数据对象时,对数据造成破坏。它是保证多线程安全访问竞争资源的一种手段。
(1)同步和锁:Java中的每个对象都有一个内置锁。当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁;当程序运行到synchronized同步代码块时,自动获得锁定对象的锁。一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放锁。这也意味着任何其他线程都不能进入synchronized方法或者代码块,直到该锁被释放。释放锁是指持锁线程退出了synchronized同步方法或者代码块。
(2)对于同步,一般而言在Java中需要完成两个操作:把竞争访问的资源表示为private;同步那些访问资源的代码,使用synchronized关键字修饰方法或者代码块。当synchronized方法执行完或者发生异常时会自动释放锁。
2、举例
(1)案例介绍
某银行账号上有500元存款,一个人拿着存折去取钱,同时另一个人拿着银行卡去ATM机上取钱,各自取钱400元。要求取钱的过程中不能出现资源竞争:比如400元被取出两次、银行卡的账目不能小于0等。
(2)案例实现
package com.iotek.synchronize;
public class BankDemo {
public static void main(String[] args) {
Bank bank1 = new Bank();
BankThread bankThread = new BankThread(bank1);
bankThread.start();//柜台取钱
BankThread bankThread2 = new BankThread(bank1);
bankThread2.start();//ATM机取钱
}
}
class Bank{
private int money = 500;
private Object object = new Object();//锁这个对象也可以实现,因为任何对象都只有一个锁。
/*取钱的方法,返回取钱的数目
当一个线程去调用同步方法的时候,这个线程就调用了当前对象的锁。其他线程则等待*/
public int getMoney(int number) throws InterruptedException{
synchronized(this){
if(number < 0)
return -1;
else if(money < 0)
return -2;
else if(number - money >0)
return -3;
else{
Thread.sleep(1000);
money-=number;
System.out.println("账户余额:" + money);
}
return number;
}
}
}
class BankThread extends Thread{
private Bank bank = null;
public BankThread(Bank bank){
this.bank = bank;
}
@Override
public void run(){
try {
System.out.println("取钱:"+bank.getMoney(400));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、同步产生死锁
(1)同步产生死锁的原因:当一个线程已经获取了对象1的锁,同时又想获取对象二的锁。而此时另一个线程当前已经持有了对象二的锁,而又想获取对象一的锁。这中相互等待对方释放锁的过程,会导致“死锁”。
(2)死锁举例
package com.iotek.synchronize;
public class DieThreadDemo {
public static void main(String[] args) throws InterruptedException {
Example example = new Example();
DieThread thread1 = new DieThread(example);
thread1.start();
DieThread1 thread2 = new DieThread1(example);
thread2.start();
}
}
class Example{
private Object obj1 = new Object();
private Object obj2 = new Object();
public void method1() throws InterruptedException{//先占用对象1,再请求对象2.
synchronized (obj1) {
Thread.sleep(1000);
synchronized (obj2) {
System.out.println("method1");
}
}
}
public void method2() throws InterruptedException{//先占用对象2,再请求对象1.
synchronized (obj2) {
Thread.sleep(1000);
synchronized (obj1) {
System.out.println("method2");
}
}
}
}
class DieThread extends Thread{//死亡进程1
private Example example = null;
public DieThread(Example example) {
super();
this.example = example;
}
@Override
public void run(){
try {
example.method1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class DieThread1 extends Thread{//死亡进程2
private Example example = null;
public DieThread1(Example example) {
super();
this.example = example;
}
@Override
public void run(){
try {
example.method2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}