synchronized关键用于多线程环境中实现操作的原子性、互斥性,确保有序的同一时间对同一资源访问。实际上,是锁的一种实现。
用法:
class ClassA{
synchronized void methodA(){//修饰非静态方法1
//临界区
}
synchronized static void methodB(){//修饰静态方法2
//临界区
}
void methodC(){
synchronized(){
//修饰代码块3,相较于修饰方法,影响的范围更小
}
}
}
修饰非静态方法时,锁定的对象是当前类的实例,修饰静态方法时,锁定的是当前类。对应于以上的1、2、3即。
synchronized(this) void …
synchronized(ClassA.class) void …
synchronized(this)…
this指的是类的一个实例,所以如果是不同实例,那么锁是无效的,无法做到资源访问的互斥。
因此,尽管在同一个类中都使用了synchronized关键字修饰资源,有时也无法做到安全的访问资源。因为资源对应的锁不是同一个。
多个相关联的资源应该作为同一个资源对应同一把锁。
例如
class ClassA{
static int val=0;
synchronized int getVal(){
return val;
}
synchronized static void addVal(){
val+=1;
}
}
以上是不安全的,因为两个synchronized所加的锁实际是不同的,不能实现资源访问的互斥。
应当改为:
class ClassA{
static int val=0;
private final Object valLock=new Object();
int getVal(){
synchronized(valLock){
return val;
}
}
static void addVal(){
synchronized(valLock){
val+=1;
}
}
}
下面再看一个转账的例子:A账户往B账户转账100,B账户往C账户转账100,每一个账户原始金额为200,线程1负责A到B,线程2负责B到C,由于线程1只对A对象加锁,线程2只对B对象加锁,两者别不互斥,可能导致两个线程读取到B中余额都是200,而最终B中余额是300.
class Account {
private int balance;
// 转账
synchronized void transfer(Account target, int amt){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
因此上面的方式不能实现线程操作的安全,应当改为如下
class Account {
private
int
balance;
// 转账
void
transfer(Account target,
intamt){
synchronized
(Account.
class) {
if
(
this.balance > amt) {
this
.balance -= amt;
target.balance += amt;
}
}
}
}
对类加锁,这样所有账户对象用同一把锁。
即使如此,可以实现多线程的安全操作,实际上依然是存在问题的。
如果Account类有多个子类AccountSon1 AccountSon2…,那么这些子类既属于自己的类型也同时是Account类型,在调用transfer方法时,上锁的实际是AccountSon1.class AccountSon2.class类型,依然存在安全问题。
那么此时,可以如此
final class Account{
……
}
原文地址:https://www.cnblogs.com/perfumeBear/p/11814125.html