并发编程之synchronize
synchronized是Java中的关键字,是一种常用的线程同步锁。
用法
注意:在理解synchronized时,要知道一个核心点,synchronized锁定的不是代码,而是对象。使用synchronized时,其会申请对象的堆内存,进行锁定。
写法一
Object o = new Object(); // 锁对象
public void test01(){
//任何线程要执行以下的代码,必须先拿到锁
synchronized (o){
// doSomething...
}
}
写法二
上述写法是创建一个锁对象,其实可以自身作为锁对象。
public void test02(){
synchronized (this){
// doSomething...
}
}
写法三
同写法二。
public synchronized void test03(){
// doSomething...
}
写法四
锁定静态方法。静态方法是属于类方法,没有对象。
public synchronized static void test04(){
}
写法五
同写法四
public static void test04(){
synchronized (SynchronizeTest.class){
}
}
测试线程安全问题
演示代码:
public class TestSynch implements Runnable{
private int count = 10;
@Override
public /*synchronized*/ void run() {
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
}
public static void main(String[] args) {
TestSynch t = new TestSynch();
for (int i = 0; i < 8; i++) {
new Thread(t,"结果是"+i).start();
}
}
}
结果是:
加入synchronized:
注意事项
-
加锁方法和不加锁方法
public class Account { int count = 100; // 写入数据 public synchronized void set(int s){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.count = s; } // 读取数据 public /*synchronized*/ void get(){ System.out.println(count); } public static void main(String[] args) { Account a = new Account(); new Thread(()->a.set(1)).start(); a.get(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } a.get(); } }
在实际业务场景中,往往将读方法不加锁,写的方法加锁,这样会导致一个问题,也就是读的数据是写之前的数据,导致脏读问题。
解决方案就是,在读方法上也加锁。
-
加锁方法不影响不加锁方法的执行;
-
加锁方法访问另外一个加锁方法,一个线程拥有某个对象的锁,再次申请的时候可以再次得到这把锁(相当于锁上了两把同样的锁);子类的同步方法调用父类的同步方法也可以;
-
synchronized 遇到异常,锁会被释放。如果不想该锁被释放,就直接catch;
-
不要以字符创常量作为锁对象。
public class StringAsSynchObject { private String stra = "hello"; private String strb = "hello"; void test1(){ synchronized (stra){ } } void test2(){ synchronized (strb){ } } }
在上述代码中,因为stra和strb 锁的是同一个对象,如果用到了同一个类库,在该类库中的代码锁定了字符串"hello",我们读不到源码,而在业务代码中也锁定了同样的字符串,这就有可能会造成非常诡异的死锁阻塞。因为程序和类库不经意用了同一把锁。(这种情况一般没办法调试)。所以通常不要字符串作为锁定对象。
-
synchronize 锁定的粒度越小(即锁定的业务代码越少),效率越高。
-
synchronize 锁释放的情况:
1)线程执行完毕;
2)线程发生异常;
3)线程进入休眠状态。
-
synchronize 是互斥锁,可重入锁。
-
wait()和notify()/notifyAll() 与 synchronize同时出现。
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10694661.html 版权声明:本文为博主原创文章,转载请附上博文链接!
本文来自博客园,作者:追梦1819,转载请注明原文链接:https://www.cnblogs.com/yanfei1819/p/10694661.html