一、多线程运行问题
1、各个线程是通过竞争CPU时间而获得运行机会的
2、各线程什么时候得到CPU时间,占用多久,是不可预测的
3、一个正在运行着的线程在什么地方被暂停是不确定的
二、线程同步
为了解决上述问题,确保共享对象在同一时间只允许被一个线程访问,即线程同步,可以使用synchronized和lock来实现。
三、synchronized的使用方式
1、修饰一个代码块,被修饰的代码块称为同步代码块,作用范围是大括号{}括起来的代码;
2、修饰一个方法,被修饰的方法称为同步方法,其作用范围是整个方法;
3、修饰一个静态方法,作用范围是整个静态方法;
例程(银行存取款)
Class Bank
package com.imooc.bank;
public class Bank {
private String account;//账号
private int balance;//账户余额
public Bank(String account,int balane){
this.account=account;
this.balance=balane;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Bank[账号:"+account+",余额:"+balance+"]";
}
//存款
public synchronized void saveAccount(){
//获取当前的账户余额
int balance=getBalance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改金额,存100元
balance+=100;
//修改账余额
setBalance(balance);
//输出存款后的账户余额
System.out.println("存款后的账户余额为:"+balance);
}
public void drawAccount(){
synchronized (this){
//获得当前账户余额
int balance=getBalance();
balance=balance-200;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setBalance(balance);
System.out.println("取款后的账户余额:"+balance);
}
}
}
Class DrawAccount 取款
package com.imooc.bank;
public class DrawAccount implements Runnable{
Bank bank;
public DrawAccount(Bank bank){
this.bank=bank;
}
@Override
public void run() {
bank.drawAccount();
}
}
Class SaveAccount 存款
package com.imooc.bank;
public class SaveAccount implements Runnable{
Bank bank;
public SaveAccount(Bank bank){
this.bank=bank;
}
@Override
public void run() {
bank.saveAccount();
}
}
Class Test 测试类
package com.imooc.bank;
public class Test {
public static void main(String[] args) {
//创建账户,给定余额为10000
Bank bank=new Bank("1001",1000);
//创建线程对象
SaveAccount sa=new SaveAccount(bank);
DrawAccount da=new DrawAccount(bank);
Thread save=new Thread(sa);
Thread draw=new Thread(da);
save.start();
draw.start();
try {
draw.join();
save.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bank);
}
}
经过测试,当不加入synchronized实现线程同步时,存款取款的账户余额会发生错误,
这里使用线程延时模拟了线程被打断的情况,当存款没完全进行完时打断了此线程进行取款
此时取款后的余额不正确。
当加入了线程同步后,此问题得到了解决。
lock的使用方式
1、synchroized同步的时候,其中一条线程用完会自动释放锁,而Lock需要手动释放,如果不手动释放,可能造成死锁
2、使用synchronized如果其中一个线程不释放锁,那么其他需要获取锁的线程会一直等待下取,直到使用完释放或者出现异常,
而Lock可以使用响应中断或者使用规定等待时间的锁
3、synchronized无法得知是否获取到锁,而Lock可以做到
4、用ReadWriteLock可以提高多个线程进行读操作的笑了
lock简单的使用方法
//创建lock对象
Lock lock=new ReentrantLock();
//手动加锁
lock.lock();
try{
// 处理
}catch(Exception ex){
// 捕获异常
}finally{
// 释放锁
lock.unlock();
}