synchronized用法:
- 修饰实例方法:当synchronized修饰实例方法时,无需显式指定锁对象,每个对象都有一个内置锁,每次只允许一个线程访问同步的实例方法
修饰符 synchronized 返回值类型 方法名(){.....}
例如:
public synchronized void synchronizedMethod() {
// 同步实例方法
}
//需要注意这种方法会为每个对象内置一个锁,
//不是同一个对象之间的锁是没有关系的,要注意使用场景,
//参考下面的买票改造案例
- 修饰代码块:synchronized还可以用来修饰代码块,被修饰的代码块称为同步语句块,其工作的作用范围是synchronized括号里配置的对象。
synchronized (Obj){执行体…}
//obj是锁对象
例如:
public void method() {
synchronized (obj) {
// 同步代码块
}
}
//在使用中一般将一个静态属性,
//或者一个不会因为创建多个对象而产生多个不同的属性设置为锁就是代码中的obj,
//参考下面银行改造代码,
//也可以将类名.class这个编译文件作为锁,
//否者每创建一个独立的对象就会创建一个新锁,就失去了意义,
下面以两个例子演示线程不安全问题。
示例1:买票问题
代码如下(示例):
// 模拟线程不安全问示例1:买票
public class MyThread{
public static void main(String[] args) throws InterruptedException {
BuyTicker ticker = new BuyTicker();
Thread person1Thread = new Thread(ticker, "person1");
Thread person2Thread = new Thread(ticker, "person2");
Thread person3Thread = new Thread(ticker, "person3");
person1Thread.start();
person2Thread.start();
person3Thread.start();
}
}
class BuyTicker implements Runnable{
// 车票
private int tickerNum = 10;
// 停止线程标识
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buyTicker();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buyTicker() throws InterruptedException {
// 判断是否还有票
if (tickerNum <= 0) {
flag = false;
return;
}
// 模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName() + "买到第" + tickerNum -- + "张票");
}
}
执行结果:可以看到第4、3张票卖了两次,还有人买到了第0张票
person3买到第8张票
person2买到第10张票
person1买到第9张票
person1买到第7张票
person3买到第5张票
person2买到第6张票
person1买到第4张票
person2买到第4张票
person3买到第3张票
person1买到第2张票
person2买到第2张票
person3买到第1张票
person1买到第0张票
示例2:银行取钱
代码如下(示例):
// 模拟线程不安全示例2:银行取钱
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Account account = new Account(1000, "旅游基金");
new Bank(account, 500, "你").start();
new Bank(account, 600, "女朋友").start();
}
}
// 账户
class Account {
// 账户总余额
int money;
// 账户名
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
// 银行
class Bank extends Thread{
// 客户账户
Account account;
// 取得钱数
int drawingMoney;
public Bank(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
if (account.money- drawingMoney <= 0) {
System.out.println(account.name+ "钱不够了,取不了了");
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 余额 - 取得钱
account.money = account.money - drawingMoney;
System.out.println(Thread.currentThread().getName() + "取了" + drawingMoney);
System.out.println(account.name + "余额为:" + account.money);
}
}
执行结果:当你取500时,线程执行到account.money = account.money - drawingMoney之前,另一个线程抢到了CPU执行权,也执行到account.money = account.money - drawingMoney之前,现在余额还是1000,继续执行1000-500-900=-400.
你取了500
女朋友取了900
旅游基金余额为:-400
旅游基金余额为:-400
示例1:买票问题(使用同步方法改造成线程安全)
代码如下(示例):
// 模拟线程不安全问示例1:买票
public class MyThread{
public static void main(String[] args) throws InterruptedException {
BuyTicker ticker = new BuyTicker();
Thread person1Thread = new Thread(ticker, "person1");
Thread person2Thread = new Thread(ticker, "person2");
Thread person3Thread = new Thread(ticker, "person3");
person1Thread.start();
person2Thread.start();
person3Thread.start();
}
}
class BuyTicker implements Runnable{
// 车票
private int tickerNum = 10;
// 停止线程标识
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buyTicker();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buyTicker() throws InterruptedException {
// 判断是否还有票
if (tickerNum <= 0) {
flag = false;
return;
}
// 模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName() + "买到第" + tickerNum -- + "张票");
}
}
执行结果:
person1买到第10张票
person1买到第9张票
person1买到第8张票
person1买到第7张票
person1买到第6张票
person1买到第5张票
person3买到第4张票
person3买到第3张票
person3买到第2张票
person2买到第1张票
示例2:银行取钱(使用同步代码块改造成线程安全)
代码如下(示例):
// 模拟线程不安全示例2:银行取钱
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Account account = new Account(1000, "旅游基金");
new Bank(account, 500, "你").start();
new Bank(account, 600, "女朋友").start();
}
}
// 账户
class Account {
// 账户总余额
int money;
// 账户名
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
// 银行
class Bank extends Thread{
// 客户账户
Account account;
// 取得钱数
int drawingMoney;
public Bank(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
synchronized (account) {
if (account.money- drawingMoney < 0) {
System.out.println(account.name+ "钱不够了,取不了了");
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 余额 - 取得钱
account.money = account.money - drawingMoney;
System.out.println(Thread.currentThread().getName() + "取了" + drawingMoney);
System.out.println(account.name + "余额为:" + account.money);
}
}
}
执行结果:你取了500后,你女朋友取600,就提示余额不足,不会出现余额为负数的情况了。这里的同步监视器是account,account才是操作的共享资源,而不是bank。
你取了500
旅游基金余额为:500
旅游基金钱不够了,取不了了