synchronized关键字详解

来源:互联网 发布:大数据产业发展对策 编辑:程序博客网 时间:2024/06/07 05:31

参考资料:Java 多线程编程核心技术——高洪岩

java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍然可以访问该object中的非加锁代码块。

一。synchronized同步方法

①方法内部的变量是线程安全,实类变量非线程安全

package com.synch;public class HalfSelNum {private int num = 0 ;synchronized public void add(String username) {if("a".equals(username)) {num = 100;try {Thread.sleep(100);System.out.println("a set over"+num);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}} else {num = 200;System.out.println("b set over"+num);}System.out.println(username+" num="+num);}}

package com.synch;public class ThreadA extends Thread{private HalfSelNum halfSelNum;public ThreadA( HalfSelNum halfSelNum) {// TODO Auto-generated constructor stubthis.halfSelNum = halfSelNum;}@Overridepublic void run() {// TODO Auto-generated method stub//super.run();halfSelNum.add("a");}}

package com.synch;public class ThreadB extends Thread{private HalfSelNum halfSelNum;public ThreadB( HalfSelNum halfSelNum) {// TODO Auto-generated constructor stubthis.halfSelNum = halfSelNum;}@Overridepublic void run() {// TODO Auto-generated method stub//super.run();halfSelNum.add("b");}}

package com.synch;public class Run {public static void main(String[] args) {HalfSelNum numRef = new HalfSelNum();HalfSelNum numRef2 = new HalfSelNum();ThreadA ta = new ThreadA(numRef);ThreadB tb = new ThreadB(numRef2);ta.start();tb.start();}}

②脏读现象

package com.synch.zd;public class PublicVar {public String username = "A";public String password = "AA";synchronized public void setValue(String username ,String password){this.username = username;try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}this.password = password;System.out.println("set method thread name is "+Thread.currentThread().getName()+";username:"+username+";password:"+password);}public void getValue(){System.out.println("get method thread name is "+Thread.currentThread().getName()+";username:"+username+";password:"+password);}}

package com.synch.zd;public class ThreadA extends Thread{public PublicVar publicVar;public ThreadA(PublicVar publicVar) {this.publicVar = publicVar;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();publicVar.setValue("B", "BB");}public static void main(String[] args) {PublicVar pv = new PublicVar();ThreadA ta = new ThreadA(pv);ta.start();try {ta.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}pv.getValue();}}

打印结果:

get method thread name is main;username:B;password:AA
       set method thread name is Thread-0;username:B;password:BB

当threadA调用同步方法setValue时,获取了setValue的所在对象的对象锁其它线程必须等threadA执行完后才可以调用setValue,但是可以调用其它非同步方法,

这时只需要在getValue方法上也加上synchronized,这样threadA调用setValue时得到了该方法所在对象的对象锁,其它线程调用getValue同步方法时就无法获取到对象锁只能等threadA释放对象锁。打印结果如下:

set method thread name is Thread-0;username:B;password:BB
       get method thread name is main;username:B;password:BB

③锁重入

synchronized有锁重入功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求该对象锁是可以得到的,线程执行service1时得到了对象锁,而service1内部又调用service2同步方法,这是要再次获取对象锁,是可以得到对象锁的。

package com.synch.cr;public class Service {synchronized public void service1() {System.out.println("service1............");service2();}synchronized public void service2() {System.out.println("service2............");service3();}synchronized public void service3() {System.out.println("service3............");}}

package com.synch.cr;public class MyThread extends Thread{public void run() {Service service = new Service();service.service1();}public static void main(String[] args) {MyThread mt = new MyThread();mt.start();}}
打印结果:

service1............

service2............
      service3............

④出现异常会自动释放锁

package com.synch.yc;import java.util.Random;public class Service {synchronized public void testMethod() {if("a".equals(Thread.currentThread().getName())) {System.out.println("threadName:"+Thread.currentThread().getName()+" run beginTime:"+System.currentTimeMillis()); while(true) {if((Math.random()+"").substring(0,8).equals("0.123456")) {System.out.println("threadName:"+Thread.currentThread().getName()+" run exception:"+System.currentTimeMillis()); Integer.parseInt("a");}}} else {System.out.println("threadName:"+Thread.currentThread().getName()+" run beginTime:"+System.currentTimeMillis()); }}}
package com.synch.yc;public class Threada extends Thread{private Service service;public Threada(Service service) {// TODO Auto-generated constructor stubthis.service = service;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();service.testMethod();}}
package com.synch.yc;public class Threadb extends Thread{private Service service;public Threadb(Service service) {// TODO Auto-generated constructor stubthis.service = service;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();service.testMethod();}}
package com.synch.yc;public class Run {public static void main(String[] args) {Service s = new Service();    Threada ta = new Threada(s);    ta.setName("a");    ta.start();    try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}    Threadb tb = new Threadb(s);    tb.setName("b");    tb.start();     }}
打印结果:

threadName:a run beginTime:1468229383956
threadName:a run exception:1468229384429
Exception in thread "a" threadName:b run beginTime:1468229384429
java.lang.NumberFormatException: For input string: "a"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.parseInt(Integer.java:527)
at com.synch.yc.Service.testMethod(Service.java:15)
at com.synch.yc.Threada.run(Threada.java:15)

线程threada出现异常并释放锁,线程theadb执行打印

⑤同步不能继承

package com.synch.extend;public class Main {synchronized public void serviceMethod() {System.out.println("in main begin threadName:"+Thread.currentThread().getName()+" time:"+System.currentTimeMillis());try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("in main end threadName:"+Thread.currentThread().getName()+" time:"+System.currentTimeMillis());}}

package com.synch.extend;public class Sub extends Main { public void serviceMethod() {System.out.println("in Sub begin threadName:"+Thread.currentThread().getName()+" time:"+System.currentTimeMillis());try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("in Sub end threadName:"+Thread.currentThread().getName()+" time:"+System.currentTimeMillis());}}
package com.synch.extend;public class Threada extends Thread{private Sub sub;public Threada( Sub sub) {// TODO Auto-generated constructor stubthis.sub = sub;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();sub.serviceMethod();}}
package com.synch.extend;public class Threadb extends Thread{private Sub sub;public Threadb( Sub sub) {// TODO Auto-generated constructor stubthis.sub = sub;}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();sub.serviceMethod();}}
package com.synch.extend;public class Client {public static void main(String[] args) {Sub sub = new Sub();Threada ta = new Threada(sub);ta.setName("a");ta.start();Threadb tb = new Threadb(sub);tb.setName("b");tb.start();}}
打印结果:

in Sub begin threadName:a time:1468230402003
in Sub begin threadName:b time:1468230402004
in Sub end threadName:b time:1468230402104
in Sub end threadName:a time:1468230402104

可以看出sub的serviceMetod方法是butongbu

二synchronized同步语句块

使用synchronized关键字声明的方法是有弊端的,比如A线程执行的同步方法需要很长时间,那么另外一个线程B想要执行这个方法就需要等待较长的时间,在这个情况下就需要使用synchronized同步语句块。

代码 :

package com.synch.dmk;public class HalfSelNum { public void add() {synchronized (this) {System.out.println("threadName:"+Thread.currentThread().getName()+" start:"+System.currentTimeMillis());try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("threadName:"+Thread.currentThread().getName()+" end:"+System.currentTimeMillis());}}}

package com.synch.dmk;public class ThreadA extends Thread{private HalfSelNum halfSelNum;public ThreadA( HalfSelNum halfSelNum) {// TODO Auto-generated constructor stubthis.halfSelNum = halfSelNum;}@Overridepublic void run() {// TODO Auto-generated method stub//super.run();halfSelNum.add();}}

package com.synch.dmk;public class ThreadB extends Thread{private HalfSelNum halfSelNum;public ThreadB( HalfSelNum halfSelNum) {// TODO Auto-generated constructor stubthis.halfSelNum = halfSelNum;}@Overridepublic void run() {// TODO Auto-generated method stub//super.run();halfSelNum.add();}}

package com.synch.dmk;public class Run {public static void main(String[] args) {HalfSelNum numRef = new HalfSelNum();ThreadA ta = new ThreadA(numRef);ThreadB tb = new ThreadB(numRef);ta.start();tb.start();}}

打印结果:

threadName:Thread-0 start:1468311785240
threadName:Thread-0 end:1468311785341
threadName:Thread-1 start:1468311785341
threadName:Thread-1 end:1468311785441


可以看出和使用synchronized 修饰的方法一样是同步执行的,效率还是没有提高

package com.synch.dmk;public class HalfSelNum {private String name1 ;private String name2 ;public void doTask() {System.out.println("begin   Task....................");try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}String str1= "start run ..........."+Thread.currentThread().getName();String str2= "start run ..........."+Thread.currentThread().getName();synchronized (this) {name1=str1;name2=str2;}System.out.println("name1=0"+name1);System.out.println("name2=0"+name2);}}

打印结果:

begin   Task....................
begin   Task....................
name1=0start run ...........Thread-1
name1=0start run ...........Thread-1
name2=0start run ...........Thread-1
name2=0start run ...........Thread-1

当一个线程访问一个对象的同步代码块时,其它线程可以访问该对象的非同步代码块

在使用synchronized(this)时要注意,当一个线程访问synchronized(this)代码块时其它的synchronized(this)代码块将被阻塞。这说明synchronized(this)使用的‘对象监视器’是一个。锁非this对象有一定的优势,若果一个类中有多个synchronized方法,这是使用锁this,会实现同步,但会阻塞,影响效率。如果使用锁非this对象。则synchronized中的程序与同步方法是异步的,不与其他this锁同步方法争抢this锁,则可以大大提高运行效率。

代码:

package com.synch.syb;public class HalfSelNum {protected String lock = new String();public void doTask1() {synchronized (lock) {System.out.println(" a begin....................");try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(" a end....................");}}synchronized public void doTask2() {System.out.println(" b begin....................");System.out.println(" b begin....................");}}

package com.synch.syb;public class ThreadA extends Thread{private HalfSelNum halfSelNum;public ThreadA( HalfSelNum halfSelNum) {// TODO Auto-generated constructor stubthis.halfSelNum = halfSelNum;}@Overridepublic void run() {// TODO Auto-generated method stub//super.run();halfSelNum.doTask1();}}

package com.synch.syb;public class ThreadB extends Thread{private HalfSelNum halfSelNum;public ThreadB( HalfSelNum halfSelNum) {// TODO Auto-generated constructor stubthis.halfSelNum = halfSelNum;}@Overridepublic void run() {// TODO Auto-generated method stub//super.run();halfSelNum.doTask2();}}

package com.synch.syb;public class Run {public static void main(String[] args) {HalfSelNum numRef = new HalfSelNum();ThreadA ta = new ThreadA(numRef);ThreadB tb = new ThreadB(numRef);ta.start();tb.start();}}
打印结果:

 a begin....................
 b begin....................
 b begin....................
 a end....................

关键字synchronized还可以对静态方法持锁,如果这样写那是对当前Java文件的Class 类进行持锁。


三。死锁

死锁是程序设计的bug,在程序设计中因避免双方互相持有对方锁,只要互相等待对方锁,就可能出现死锁。
死锁示例:
package com.synch.ss;public class HalfSelNum extends Thread{private String  username ;private String locka = new String();private String lockb = new String(); public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public void run()  {if("a".equals(username)) {synchronized (locka) {System.out.println("a start time:"+System.currentTimeMillis());try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (lockb) {System.out.println("lockb 获取到了");}}} else {synchronized (lockb) {System.out.println("b start time:"+System.currentTimeMillis());try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (locka) {System.out.println("locka 获取到了");}}}}public static void main(String[] args) {HalfSelNum a = new HalfSelNum();a.setUsername("a");Thread ta = new Thread(a);ta.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}a.setUsername("b");Thread tb = new Thread(a);tb.start();}}





0 0
原创粉丝点击