在多线程问题中,Synchronized是解决线程同步的一种手段。
说Synchronized之前,还有解决线程同步的手段有Lock、Volatile、
wait() notify()方法 、CAS(硬件CPU同步原语Compare And Swap)
先说一下Sychronized的用法:
1、修饰一个类:作用于对这个类所有的对象进行加锁
2、修饰一个方法,被修饰的方法称为同步方法,作用于调用这个方法的对象
3、修饰一个静态方法:静态方法属于类方法,作用于所以对这个类所有对象加锁
4、修饰代码块:被修饰的代码块属于同步语句,作用于调用这个代码块的对象
结论:
Synchronized方法是一种粗粒度的并发控制,某一时刻,智能有一个线
程执行该synchronized方法。
Synchronized块是一种细粒度的并发控制,只会将块中的代码同步,位
于方法内,synchronized块以外的其他代码可以被多个线程同时访问到
package tw.com;
class SynObj {
/*
* methodA方法是对当前对象加锁,由于线程1在休眠,还没释放锁,
* 导致线程2只能在5秒后调用B;线程2是对对象this进行加锁
* 可以看出两个加锁用的是同一个锁对象
*/
public synchronized void methodA() {
System.out.println("methodA.....");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
synchronized(this) {
System.out.println("methodB.....");
}
}
public void methodC() {
String str = "sss";
synchronized (str) {
System.out.println( "methodC.....");
}
}
}
public class Main {
public static void main(String[] argv) {
final SynObj obj = new SynObj();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
obj.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
obj.methodB();
}
});
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
obj.methodC();
}
});
t3.start();
}
}
输出结果
methodA.....
methodC.....
methodB.....
Synchronized锁的使用主要是判断是为对象还是类进行加锁,若两个方
法都是对当前对象加锁,则两个线程同时访问一个对象的两个方法时会发
生竞争,必须等待一个执行完后另一个才开始执行,若是两个进程同时访
问不同对象的两个方法时则不会竞争;若两个方法是对当前类加锁,若同
时两个线程同时访问同一对象的两个方法或者不同对象的两个方法都会发
生竞争
还有一点同步方法是直接在方法上加Synchronized实现加锁,同步代码块则在方法内部加锁,很显然,同步方法锁的范围比较大,同步代码块范围要小。一般来说同步的范围越大,性能越差。
JDK中对Synchronized的描述
HashMap和HashTable的get方法区别
* @see #put(Object, Object)
*/
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
Hashtable中很多方法都被Synchronized修饰,这样可以说明即使在没
有冲突的时候也需要等待执行,这样的话效率就低。
使用collections的synchronizedMap将不安全的Hashmap转化为线程安全的Hashmap
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
private static class SynchronizedMap<K,V>
implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K,V> m; // Backing Map
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
this.m = Objects.requireNonNull(m);
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized (mutex) {return m.remove(key);}
}
在构造函数中定义mutex=this,mutex定义为final类型,具体方法执行
了synchronized(mutex),这样会对调用该方法的对象进行加锁。这
样的话效率也低。