synchronized二三事1
来源:互联网 发布:淘宝付邮试用中心入口 编辑:程序博客网 时间:2024/06/07 04:52
1.首先非线程安全指的是成员变量 private int x,此处成员是private 的 否则synchronized就不能防止其他线程访问了
2.必须记住的
.Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
(1)当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
(2)然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
(3)当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
(4)第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
(5)synchronized取得的锁都是对象锁,而不是代码段或者方法的锁,哪个线程先执行synchronized块就获得该对象锁,其他访问该对象的同步操作就要等待,前提要记住是访问同一个对象。如果多个线程访问多个对象,那就产生了多个对象锁。
(6)以上规则对其它对象锁同样适用.
package ths;public class Thread1 implements Runnable { public void run() { synchronized(this) { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " synchronized loop " + i); } } } public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread ta = new Thread(t1, "A"); Thread tb = new Thread(t1, "B"); ta.start(); tb.start(); } }结果: A synchronized loop 0 A synchronized loop 1 A synchronized loop 2 A synchronized loop 3 A synchronized loop 4 B synchronized loop 0 B synchronized loop 1 B synchronized loop 2 B synchronized loop 3 B synchronized loop 4
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。package ths;public class Thread2 { public void m4t1() { synchronized(this) { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } } public void m4t2() { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } public static void main(String[] args) { final Thread2 myt2 = new Thread2(); Thread t1 = new Thread( new Runnable() { public void run() { myt2.m4t1(); } }, "t1" ); Thread t2 = new Thread( new Runnable() { public void run() { myt2.m4t2(); } }, "t2" ); t1.start(); t2.start(); } }结果: t1 : 4 t2 : 4 t1 : 3 t2 : 3 t1 : 2 t2 : 2 t1 : 1 t2 : 1 t1 : 0 t2 : 0
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。 //修改Thread2.m4t2()方法: public void m4t2() { synchronized(this) { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } }结果: t1 : 4 t1 : 3 t1 : 2 t1 : 1 t1 : 0 t2 : 4 t2 : 3 t2 : 2 t2 : 1 t2 : 0
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。 //修改Thread2.m4t2()方法如下: public synchronized void m4t2() { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } }结果: t1 : 4 t1 : 3 t1 : 2 t1 : 1 t1 : 0 t2 : 4 t2 : 3 t2 : 2 t2 : 1 t2 : 0
五、以上规则对其它对象锁同样适用:package ths;public class Thread3 { class Inner { private void m4t1() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } } private void m4t2() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } } } private void m4t1(Inner inner) { synchronized(inner) { //使用对象锁 inner.m4t1(); } private void m4t2(Inner inner) { inner.m4t2(); } public static void main(String[] args) { final Thread3 myt3 = new Thread3(); final Inner inner = myt3.new Inner(); Thread t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1"); Thread t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2"); t1.start(); t2.start(); } }结果:尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。 t1 : Inner.m4t1()=4 t2 : Inner.m4t2()=4 t1 : Inner.m4t1()=3 t2 : Inner.m4t2()=3 t1 : Inner.m4t1()=2 t2 : Inner.m4t2()=2 t1 : Inner.m4t1()=1 t2 : Inner.m4t2()=1 t1 : Inner.m4t1()=0 t2 : Inner.m4t2()=0现在在Inner.m4t2()前面加上synchronized: private synchronized void m4t2() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } }结果:尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2对Inner.m4t2()的访问也被阻塞,因为m4t2()是Inner中的一个同步方法。 t1 : Inner.m4t1()=4 t1 : Inner.m4t1()=3 t1 : Inner.m4t1()=2 t1 : Inner.m4t1()=1 t1 : Inner.m4t1()=0 t2 : Inner.m4t2()=4 t2 : Inner.m4t2()=3 t2 : Inner.m4t2()=2 t2 : Inner.m4t2()=1 t2 : Inner.m4t2()=0
3.synchronized是可重入的
就是说线程A获得对象锁后,再次请求该对象的对象锁时会得到,这也证明在一个synchronized方法中调用另一个synchronized方法是可以获得锁的。就是自己可以获得自己的内部锁。如果是不可重人的话,就会产生死锁。可重入锁也支持继承环境,子类可以重入调用父类的同步方法。例如:
/** * @Title: Run.java * @Package syn * @Description: TODO(用一句话描述该文件做什么) * @author LingLee * @date 2017年3月22日 下午10:34:58 * @version V1.0 */ package syn;/** * @Title: Run * @Description: TODO(用一句话描述该文件做什么) */class Main {public int i=10;synchronized public void mainMethod(){i--;System.out.println("main i="+i);try {Thread.sleep(200);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class Sub extends Main{synchronized public void sunMethod(){try{while(i>0){i--;//System.out.println("sub i="+i);Thread.sleep(200);this.mainMethod();//继承父类的synchronized方法, 两个方法都是i--,但是同步}}catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}}public class Run extends Thread{public void run(){Sub s=new Sub();s.sunMethod();}public static void main(String[] args){new Run().start();//new Run().start();}}
sub i=9main i=8sub i=7main i=6sub i=5main i=4sub i=3main i=2sub i=1main i=0
4.同步不具有继承性
这里和3不一样,要好好理解。一个同步方法method,在子类中重写这个方法,并调用了super.method()函数。但是子类的method方法仍是非同步的,如果要同步的话,子类的method方法必须加上synchronized修饰。而子类直接调用父类的method是具有同步的(可重入)。见例子
/** * @Title: Run.java * @Package syn * @Description: TODO(用一句话描述该文件做什么) * @author LingLee * @date 2017年3月22日 下午10:34:58 * @version V1.0 */ package syn;/** * @Title: Run * @Description: TODO(用一句话描述该文件做什么) */class Main {public int i=10;synchronized public void mainMethod(){//System.out.println()i--;System.out.println("main i="+i+Thread.currentThread().getName());try {Thread.sleep(200);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class Sub extends Main{synchronized public void sunMethod(){try{while(i>0){i--;//System.out.println("sub i="+i);Thread.sleep(200);this.mainMethod();//继承父类的synchronized方法, 两个方法都是i--,但是同步}}catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}public void mainMethod(){//**************此处加上synchronized修饰则是同步的//System.out.println()i--;System.out.println("sub i="+i+Thread.currentThread().getName());super.mainMethod();try {Thread.sleep(200);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public class Run extends Thread{public void run(){Sub s=new Sub();s.mainMethod();}public static void main(String[] args){new Run().start();new Run().start();}}
sub i=9Thread-0sub i=9Thread-1main i=8Thread-1main i=8Thread-0
5.脏读:赋值操作set虽然是synchronized修饰的,但是get方法却是非同步的,那么当读实例变量时,该实例变量可能因为set发生了改变,这种情况就是脏读,解决方案就是get加上synchronized
- synchronized二三事1
- synchronized二三事2
- synchronized二三事3
- synchronized二三事4
- Java Synchronized用法1
- 线程同步1-synchronized
- synchronized学习笔记1
- 关于 @synchronized-------1
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- L1-009. N个数求和
- Elasticsearch(三)【.Net客户端API规范--生命周期】
- C++:面向行的类成员函数
- pwd命令
- HTML5中的Range对象的研究
- synchronized二三事1
- Python_08
- Java初认识
- Android插件—Android Drawable Importer
- linux 中大部分命令是如何工作的
- 报错:指定的架构无效.所有 SSDL 项目都必须以同一提供程序为目标。ProviderManifestToken“2012”不同于以前遇到的“2005”。
- 回文数字
- 非搜索二叉树的最近公共祖先
- IO高级应用-BufferedReader