本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
为仪陇等地区用户提供了全套网页设计制作服务,及仪陇网站建设行业解决方案。主营业务为成都做网站、网站制作、仪陇网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
synchronized是Java中的一个关键字,在多线程共同操作共享资源的情况下,可以保证在同一时刻只有一个线程可以对共享资源进行操作,从而实现共享资源的线程安全。
在应用Sychronized关键字时需要把握如下注意点:
一把锁只能同时被一个线程获取,没有获得锁的线程只能等待;
每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁;
synchronized修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁。
对象锁
包括方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象锁)
代码块形式:手动指定锁定对象,也可是是this,也可以是自定义的锁
- public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence = new SynchronizedObjectLock(); // 创建2把锁 Object block1 = new Object(); Object block2 = new Object(); @Override public void run() { // 这个代码块使用的是第一把锁,当他释放后,后面的代码块由于使用的是第二把锁,因此可以马上执行 synchronized (block1) { System.out.println("block1锁,我是线程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("block1锁,"+Thread.currentThread().getName() + "结束"); } synchronized (block2) { System.out.println("block2锁,我是线程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("block2锁,"+Thread.currentThread().getName() + "结束"); } } public static void main(String[] args) { Thread t1 = new Thread(instence); Thread t2 = new Thread(instence); t1.start(); t2.start(); } } 复制代码
输出结果:
- block1锁,我是线程Thread-0 block1锁,Thread-0结束 block2锁,我是线程Thread-0 // 可以看到当第一个线程在执行完第一段同步代码块之后,第二个同步代码块可以马上得到执行,因为他们使用的锁不是同一把 block1锁,我是线程Thread-1 block2锁,Thread-0结束 block1锁,Thread-1结束 block2锁,我是线程Thread-1 block2锁,Thread-1结束
方法锁形式:synchronized修饰普通方法,锁对象默认为this
- public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence = new SynchronizedObjectLock(); @Override public void run() { method(); } public synchronized void method() { System.out.println("我是线程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束"); } public static void main(String[] args) { Thread t1 = new Thread(instence); Thread t2 = new Thread(instence); t1.start(); t2.start(); } }
类锁
包括方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象)
synchronize修饰静态方法(类的class对象)
- public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence1 = new SynchronizedObjectLock(); static SynchronizedObjectLock instence2 = new SynchronizedObjectLock(); @Override public void run() { method(); } // synchronized用在静态方法上,默认的锁就是当前所在的Class类,所以无论是哪个线程访问它,需要的锁都只有一把 public static synchronized void method() { System.out.println("我是线程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束"); } public static void main(String[] args) { Thread t1 = new Thread(instence1); Thread t2 = new Thread(instence2); t1.start(); t2.start(); } }
输出结果:
我是线程Thread-0 Thread-0结束 我是线程Thread-1 Thread-1结束 复制代码
- /** * synchronized修饰实例方法,当线程拿到锁,其他线程无法拿到该对象的锁,那么其他线程就无法访问该对象的其他同步方法 * 但是可以访问该对象的其他非synchronized方法 * 锁住的是类的实例对象 */ public class synchronizedDemo1 implements Runnable { //模拟一个共享数据 private static int total=0; //同步方法,每个线程获取到锁之后,执行5次累加操作 public synchronized void increase(){ for (int i = 1; i < 6; i++) { System.out.println(Thread.currentThread().getName()+"执行累加操作..."+"第"+i+"次累加"); try { total=total+1; Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } //实例对象的另一个同步方法 public synchronized void declare(){ System.out.println(Thread.currentThread().getName()+"执行total-1"); total--; System.out.println(Thread.currentThread().getName()+"执行total-1完成"); } //普通实例方法 public void simpleMethod(){ System.out.println(Thread.currentThread().getName()+ " ----实例对象的普通方法---"); } @Override public void run() { //线程执行体 System.out.println(Thread.currentThread().getName()+"准备执行累加,还没获取到锁"); //执行普通方法 simpleMethod(); //调用同步方法执行累加操作 increase(); //执行完increase同步方法后,会释放掉锁,然后线程1和线程2会再一次进行锁的竞争,谁先竞争得到锁,谁就先执行declare同步方法 System.out.println(Thread.currentThread().getName()+"完成累加操作"); //调用实例对象的另一个同步方法 System.out.println(Thread.currentThread().getName()+"准备执行total-1"); declare(); } public static void main(String[] args) throws InterruptedException { synchronizedDemo1 syn = new synchronizedDemo1(); Thread thread1 = new Thread(syn,"线程1"); Thread thread2 = new Thread(syn,"线程2"); thread1.start(); thread2.start(); } }
输出结果:
线程1准备执行累加,还没获取到锁 线程2准备执行累加,还没获取到锁 线程2 ----实例对象的普通方法--- 线程2执行累加操作...第1次累加 //线程2通过与线程1的竞争率先拿到了锁,进入increase同步方法 线程2执行累加操作...第2次累加 线程1 ----实例对象的普通方法--- //从这里可看出,在线程2访问同步方法时,线程1是可以访问非同步方法的,但是不可以访问另外一个同步方法 线程2执行累加操作...第3次累加 线程2执行累加操作...第4次累加 线程2执行累加操作...第5次累加 线程2完成累加操作 //线程2执行累加后会释放掉锁 线程2准备执行total-1 线程1执行累加操作...第1次累加 //然后线程1拿到锁后进入increase同步方法执行累加 线程1执行累加操作...第2次累加 线程1执行累加操作...第3次累加 线程1执行累加操作...第4次累加 线程1执行累加操作...第5次累加 线程1完成累加操作 //线程1完成累加操作也会释放掉锁,然后线程1和线程2会再进行一次锁竞争 线程1准备执行total-1 线程2执行total-1 //线程2通过竞争率先拿到锁进入declear方法执行total-1操作 线程2执行total-1完成 线程1执行total-1 线程1执行total-1完成 复制代码
synchronized同步是通过monitorenter和monitorexit等指令实现的,会让对象在执行,使其锁计数器加1或者减1。
monitorenter指令:每一个对象在同一时间只与一个monitor(锁)相关联,而一个monitor在同一时间只能被一个线程获得,一个对象在尝试获得与这个对象相关联的Monitor锁的所有权的时候,会发生如下3种情况之一:
monitorexit指令:释放对于monitor的所有权,释放过程很简单,就是讲monitor的计数器减1,如果减完以后,计数器不是0,则代表刚才是重入进来的,当前线程还继续持有这把锁的所有权,如果计数器变成0,则代表当前线程不再拥有该monitor的所有权,即释放锁。
对象、对象监视器、同步队列以及执行线程状态之间的关系:
该图可以看出,任意线程对Object的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态,线程状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得线程就会有机会重新获取该监视器。
可重入原理:加锁次数计数器
从上图中就可以看出来,执行静态同步方法的时候就只有一条monitorexit指令,并没有monitorenter获取锁的指令。这就是锁的重入性,即在同一锁程中,线程不需要再次获取同一把锁。
Synchronized先天具有重入性。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一。
保证可见性的原理:内存模型和happens-before规则
Synchronized的happens-before规则,即监视器锁规则:对同一个监视器的解锁,happens-before于对该监视器的加锁。
public class MonitorDemo { private int a = 0; public synchronized void writer() { // 1 a++; // 2 } // 3 public synchronized void reader() { // 4 int i = a; // 5 } // 6 } 复制代码
happens-before关系如图所示:
在图中每一个箭头连接的两个节点就代表之间的happens-before关系,黑色的是通过程序顺序规则推导出来,红色的为监视器锁规则推导而出:线程A释放锁happens-before线程B加锁,蓝色的则是通过程序顺序规则和监视器锁规则推测出来happens-befor关系,通过传递性规则进一步推导的happens-before关系。
不过两者的本质都是对对象监视器 monitor 的获取。
使用Synchronized有哪些要注意的?
当前文章:Android进阶之彻底理解Synchronized关键字
新闻来源:http://www.mswzjz.cn/qtweb/news18/72168.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能