synchronized

来源:互联网 发布:java pdf生成 编辑:程序博客网 时间:2024/06/15 19:12

1.synchronized同步方法

1.1方法内部私有变量为线程安全,实例变量非线程安全

public static void main(String[] args) {     PrivateNum num = new PrivateNum();    MyThreadA a = new MyThreadA(num);    MyThreadB b = new MyThreadB(num);    a.start();    b.start(); } public static class PrivateNum {     public void addnum(String name) {                    //重点是num变量是addnum这个方法的局部变量        int num = 0;              try {            if ("a".equals(name)) {                num = 100;                System.out.println("a ser over!");                Thread.sleep(2000);            } else {                num = 200;                System.out.println("b set over!");            }            System.out.println("线程" + name + "的num=" + num);        } catch (Exception e) {            e.printStackTrace();        }     } } public static class MyThreadA extends Thread {     private PrivateNum privateNum;     public MyThreadA(PrivateNum privateNum) {        super();        this.privateNum = privateNum;    }     @Override    public void run() {         super.run();        privateNum.addnum("a");     }} public static class MyThreadB extends Thread {     private PrivateNum privateNum;     public MyThreadB(PrivateNum privateNum) {        super();        this.privateNum = privateNum;    }     @Override    public void run() {         super.run();        privateNum.addnum("b");     }}
输出:

如果我们把num这个私有变量提到方法外边

public static class GetNum {            //之所以会出现“非线程安全”的现象,是因为num这个变量是GetNUm类的全局变量    private int num = 0;     public void getNum(String name) {        try {             if ("a".equals(name)) {                num = 100;                System.out.println("a set over");                                                                  执行到这里的时候,线程a休眠了,此时num=100,接着线程b执行了,执行的结果把num重新赋值为num=200了,线程b执行完了之后,休眠的2000毫秒也过了,                                         线程a会接着执行,而不是重新执行,所以这个时候的num仍然是200,发生了“非线程安全问题”                Thread.sleep(2000);             } else {                num = 200;                System.out.println("b set over");            }             System.out.println("线程" + name + "的num=" + num);        } catch (Exception e) {            e.printStackTrace();        }    }}
就会出现非线程安全问题:


为了防止线程安全问题,可以在方法前面加上synchronized关键字

public static class GetNum {            //之所以会出现“非线程安全”的现象,是因为num这个变量是GetNUm类的全局变量    private int num = 0;     synchronized public void getNum(String name) {        try {             if ("a".equals(name)) {                num = 100;                System.out.println("a set over");                                                                  执行到这里的时候,线程a休眠了,此时num=100,接着线程b执行了,执行的结果把num重新赋值为num=200了,线程b执行完了之后,休眠的2000毫秒也过了,                                         线程a会接着执行,而不是重新执行,所以这个时候的num仍然是200,发生了“非线程安全问题”                Thread.sleep(2000);             } else {                num = 200;                System.out.println("b set over");            }             System.out.println("线程" + name + "的num=" + num);        } catch (Exception e) {            e.printStackTrace();        }    }}
输出:
a set over线程a的num=100b set over 线程b的num=200
1.2synchronized取得的都是对象锁
如果多个线程访问多个对象,JVM创建多个锁,运行顺序是可以交叉的

package Thread10;public class HasSelfPrivateNum {   private int num=0;   synchronized public void addI(String username){   try {   if(username.equals("a")){   num=100;   System.out.println("a结束");   Thread.sleep(2000);}   else {num=200;System.out.println("b结束");}   System.out.println(username+"num"+num);   } catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}  }}package Thread10;public class ThreadA extends Thread {   private HasSelfPrivateNum numRef;   public ThreadA(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub    super();    this.numRef=numRef;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("a");}}package Thread10;public class ThreadB extends Thread {   private HasSelfPrivateNum numRef;   public ThreadB(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub    super();    this.numRef=numRef;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("b");}}package Thread10;public class Run {  public static void main(String[] args) {HasSelfPrivateNum test1=new HasSelfPrivateNum();HasSelfPrivateNum test2=new HasSelfPrivateNum();ThreadA a=new ThreadA(test1);ThreadB b=new ThreadB(test1);a.start();b.start();}}
1.3A线程持有object对象的Lock锁,B线程可以调用object对象的非synchronized方法,但不可以调用object任何的synchronized方法。

示例:

package Thread9;public class ThreadA extends Thread {   private MyObject object;   public ThreadA(MyObject object) {// TODO Auto-generated constructor stub   super();   this.object=object;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();        object.methodA();   }}package Thread9;public class ThreadB extends Thread {   private MyObject object;   public ThreadB(MyObject object) {// TODO Auto-generated constructor stub   super();   this.object=object;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();        object.methodB();   }}package Thread9;public class MyObject {    synchronized public void methodA()    {           try {       System.out.println("开始"+Thread.currentThread().getName());Thread.sleep(5000);System.out.println("结束a"+System.currentTimeMillis());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}    }    synchronized public void methodB(){  //  public void methodB(){    try {    System.out.println("线程"+Thread.currentThread().getName()+System.currentTimeMillis());Thread.sleep(5000);System.out.println("B结束");} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}      }}package Thread9;public class Run {  public static void main(String[] args) {MyObject test=new MyObject();ThreadA a=new ThreadA(test);a.setName("A");ThreadB b=new ThreadB(test);b.setName("B");a.start();b.start();}}
当运行红色部分时,输出:

开始A结束a1507091234956线程B1507091234956B结束

此时线程B也没法访问methodB方法

当运行蓝色部分时,输出:
开始A线程B1507091270428B结束结束a1507091275428
methodB方法没有加synchronized所以可以在A线程持有锁时继续调用非synchronized方法。
1.4synchronized锁重入

可重入锁:自己可以再次获取自己的内部锁,在父子类继承环境中,子类是完全可以通过"可重入锁"调用父类的同步方法的。

在一个线程得到一个对象锁时,再次请求对象锁是可以再次得到该对象的锁的,举个简单的栗子吧:

package Thread11;public class Demo {    synchronized public void service1(){      System.out.println("service1");      service2();        }    synchronized public void service2(){        System.out.println("service2");    }}package Thread11;public class MyThread extends Thread {   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();Demo test=new Demo();test.service1();}}package Thread11;public class Run {    public static void main(String[] args) {MyThread t=new MyThread();t.start();}}
输出:

service1service2
1.5出现异常,锁自动释放
当一个线程执行的代码出现异常时,其持有的锁会自动释放。

代码:

package Thread10;public class HasSelfPrivateNum {   private int num=0;   synchronized public void addI(String username){   try {   if(username.equals("a")){   System.out.println(100/0);   Thread.sleep(2000);}   else {num=200;System.out.println("b结束");}   System.out.println(username+"num"+num);   } catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}  }}package Thread10;public class ThreadA extends Thread {   private HasSelfPrivateNum numRef;   public ThreadA(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub    super();    this.numRef=numRef;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("a");}}package Thread10;public class ThreadB extends Thread {   private HasSelfPrivateNum numRef;   public ThreadB(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub    super();    this.numRef=numRef;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("b");}}package Thread10;public class Run {  public static void main(String[] args) {HasSelfPrivateNum test1=new HasSelfPrivateNum();HasSelfPrivateNum test2=new HasSelfPrivateNum();ThreadA a=new ThreadA(test1);ThreadB b=new ThreadB(test1);a.start();b.start();}}

输出:

b结束bnum200Exception in thread "Thread-0" java.lang.ArithmeticException: / by zeroat Thread10.HasSelfPrivateNum.addI(HasSelfPrivateNum.java:8)at Thread10.ThreadA.run(ThreadA.java:13)

1.6当子类重写父类方法的时候,如果不加入同步标志,一样不具备同步性。

如果父类 、子类中存在同样的方法。父类方法是同步的,子类是不同步的。那么子类的方法就不会继承父类的重载方法性质。必须自己加上synchronized关键字。

package Thread11;public class SynchronizationDoesNotHaveInheritance {    public static void main(String[] args) throws InterruptedException {          Sub sub = new Sub();          Father father = new Father();          ThreadOne threadOne = new ThreadOne(sub);          Thread thread = new Thread(threadOne);          thread.start();          ThreadTwo threadTwo = new ThreadTwo(sub);          Thread thread2 = new Thread(threadTwo);          thread2.start();      }  }    class ThreadOne implements Runnable {        private Father father;        public ThreadOne(Father sub) {          this.father = sub;      }        @Override      public void run() {          father.service();      }  }    class ThreadTwo implements Runnable {        private Father father;        public ThreadTwo(Father father) {          this.father = father;      }        @Override      public void run() {          father.service();      }  }    class Father {      protected int count = 0;        public synchronized void service() {          for (int i = 0; i < 5; i++) {              System.out.println("Thread name1:" + Thread.currentThread().getName() + " count:" + count++);              try {                  Thread.sleep(100);              } catch (InterruptedException e) {                  e.printStackTrace();              }          }      }  }    class Sub extends Father {      @Override      public void service() {          for (int i = 0; i < 5; i++) {              System.out.println("Thread name2:" + Thread.currentThread().getName() + " count:" + count++);              try {                  Thread.sleep(100);              } catch (InterruptedException e) {                  e.printStackTrace();              }          }      } }
输出:
Thread name2:Thread-0 count:0Thread name2:Thread-1 count:1Thread name2:Thread-0 count:3Thread name2:Thread-1 count:2Thread name2:Thread-1 count:4Thread name2:Thread-0 count:5Thread name2:Thread-0 count:6Thread name2:Thread-1 count:7Thread name2:Thread-1 count:8Thread name2:Thread-0 count:8
2.synchronized同步语句块

2.1 synchronized(this)是锁定的当前对象

当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。

package Thread12;public class SynObj {   public synchronized void methodA() {        System.out.println("methodA.....");        System.out.println("A时间是"+System.currentTimeMillis());        try {            Thread.sleep(5555);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public  void methodB() {        synchronized(this) {            System.out.println("methodB.....");            System.out.println("B时间是"+System.currentTimeMillis());        }    }    public void methodC() {        String str = "sss";  //string容易引发问题,慎重使用        synchronized (str) {            System.out.println("methodC.....");            System.out.println("c时间是"+System.currentTimeMillis());        }    }}package Thread12;public class TestSyn { public static void main(String[] args) {        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.....A时间是1507108745427methodC.....c时间是1507108745428methodB.....B时间是1507108750986
根据输出可以看出:A和C时间相当,而B等A释放了锁才运行,说明synchronized和synchronized方法一样是锁对象的。
2.2将任意对象作为对象监视器

在多个线程持有"对象监视器"是同一个对象的前提下,同一时间只有一个线程可以执行synchronized(这个对象)中的代码,如果对象监视器不是同一个对象,就会交叉执行。

package Thread13;public class ThreadA extends Thread {   private HasSelfPrivateNum numRef;   public ThreadA(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub    super();    this.numRef=numRef;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.methodA();}}package Thread13;public class ThreadB extends Thread {   private HasSelfPrivateNum numRef;   public ThreadB(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub    super();    this.numRef=numRef;   }   @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.methodB();}}package Thread13;import org.omg.CORBA.PRIVATE_MEMBER;public class HasSelfPrivateNum {   private String test =new String();   private String test1 =new String();   public void methodA(){    try {    synchronized (test) {    System.out.println("a开始");Thread.sleep(1000);System.out.println("a结束");    }} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}   public void methodB(){    try {    synchronized (test) {    //synchronized (test1) {    System.out.println("b开始");Thread.sleep(1000);System.out.println("b结束");    }} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}package Thread13;public class Run {      public static void main(String[] args) {HasSelfPrivateNum test=new HasSelfPrivateNum();ThreadA a=new ThreadA(test);ThreadB b=new ThreadB(test);a.start();b.start();}}
如果我们保留蓝色部分,说明是同一个对象,此时输出:

b开始b结束a开始a结束
我们保留红色部分时,不是同一个对象,交替运行:

a开始b开始a结束b结束







0 0