JAVA线程同步方法和同步代码块

JAVA 线程同步方法和同步代码块

线程安全和非线程安全

脏读

非线程安全:多个线程对同一个对象的中的实例变量进行并发访问,产生后果就是脏读,也就是获取的数据被更改。

非线程安全问题存在与“实例变量”中,如果是方法内部的私有变量,就不存在“非线程安全问题”。

线程安全:获得实例变量的值是经过同步处理的,不会出现脏读现象。

结论:只有共享资源才需要被同步,如果不是共享资源,则没有必要同步。

Synchronized同步方法

使用Synchronized关键字

方法内部变量,不存在非线程安全问题,以下代码不会发生“非线程安全”问题:

public class ThreadA extends Thread {

   private HasSelfPrivateNum numRef;

   public ThreadA(HasSelfPrivateNum numRef) {
      super();
      this.numRef = numRef;
   }

   @Override
   public void run() {
      super.run();
      numRef.addI("a");
   }

}

 

public class ThreadB extends Thread {



   private HasSelfPrivateNum numRef;

public class ThreadB extends Thread {



   private HasSelfPrivateNum numRef;



   public ThreadB(HasSelfPrivateNum numRef) {

      super();

      this.numRef = numRef;

   }



   @Override

   public void run() {

      super.run();

      numRef.addI("b");

   }



}


   public ThreadB(HasSelfPrivateNum numRef) {

      super();

      this.numRef = numRef;

   }



   @Override

   public void run() {

      super.run();

      numRef.addI("b");

   }



}

 

public class HasSelfPrivateNum {



   public void addI(String username) {

      try {

         int num; /////////num定义在在方法内部

         if (username.equals("a")) {

            num = 100;

            System.out.println("a set over!");

            Thread.sleep(2000);

         } else {

            num = 200;

            System.out.println("b set over!");

         }

         System.out.println(username + " num=" + num);

      } catch (InterruptedException e) {

         // TODO Auto-generated catch block

         e.printStackTrace();

      }

   }



}

 

public class Run {



   public static void main(String[] args) {



      HasSelfPrivateNum numRef = new HasSelfPrivateNum();



      ThreadA athread = new ThreadA(numRef);

      athread.start();



      ThreadB bthread = new ThreadB(numRef);

      bthread.start();



   }



}

运行结果  互不影响:   

  a set over!

b set over!

b num=200

a num=100

        

 

多个线程访问同1个对象的实例变量,则有可能出现“非线程安全”问题

,用线程访问的对象中如果有多个实例变量,则运行结果会有可能出现交叉的情况。

以下代码会发生“非线程安全”问题:

 

public class ThreadA extends Thread {



   private HasSelfPrivateNum numRef;



   public ThreadA(HasSelfPrivateNum numRef) {

      super();

      this.numRef = numRef;

   }



   @Override

   public void run() {

      super.run();

      numRef.addI("a");

   }



}
public class ThreadB extends Thread {



   private HasSelfPrivateNum numRef;



   public ThreadB(HasSelfPrivateNum numRef) {

      super();

      this.numRef = numRef;

   }



   @Override

   public void run() {

      super.run();

      numRef.addI("b");

   }



}
public class HasSelfPrivateNum {



   private int num = 0;/////////num定义在方法外部



   public void addI(String username) {

      try {

         if (username.equals("a")) {

            num = 100;

            System.out.println("a set over!");

            Thread.sleep(2000);

         } else {

            num = 200;

            System.out.println("b set over!");

         }

         System.out.println(username + " num=" + num);

      } catch (InterruptedException e) {

         // TODO Auto-generated catch block

         e.printStackTrace();

      }

   }



}
 
public class Run {



   public static void main(String[] args) {



      HasSelfPrivateNum numRef = new HasSelfPrivateNum();



      ThreadA athread = new ThreadA(numRef);

      athread.start();



      ThreadB bthread = new ThreadB(numRef);

      bthread.start();



   }



}

 

运行结果: 发生了脏读

                   a set over!

                   b set over!

                   a num=200

                   b num=200

 

此例如果需要解决“非线程安全”问题,需要在addI(String username)前加上关键字synchronized即可。代码如下:

synchronized public void addI(String username) {

   try {

      if (username.equals("a")) {

         num = 100;

         System.out.println("a set over!");

         Thread.sleep(2000);

      } else {

         num = 200;

         System.out.println("b set over!");

      }

      System.out.println(username + " num=" + num);

   } catch (Exception e) {

      // TODO Auto-generated catch block

      e.printStackTrace();

   }

}

 

结论:在两个线程访问同一个对象中的同步方法时,一定是线程安全的。

 

如果把上述代码中的Run类修改为:

public class Run {

   public static void main(String[] args) {

      HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
      HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();

      ThreadA athread = new ThreadA(numRef1);
      athread.start();

      ThreadB bthread = new ThreadB(numRef2);
      bthread.start();

   }

}
 

运行结果为异步:

a set over!

b set over!

b num=200

a num=100

        

这是因为,在run中创建了两个对象,分别对应两个线程,也就是产生了两把锁,互不影响。

这也说明另一个问题,通过关键组synchronized获取的锁都是对象锁,而不是把方法作为锁。

 

Synchronized方法和非synchronized方法并存

如果一个类中,部分方法为Synchronized方法,其他方法为非synchronized方法,该如何,看以下代码:

public class MyObject {



   synchronized public void methodA() {

      try {

         System.out.println("begin methodA threadName="

               + Thread.currentThread().getName());

         Thread.sleep(5000);

         System.out.println("end endTime=" + System.currentTimeMillis());

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

   }



   public void methodB() {

      try {

         System.out.println("begin methodB threadName="

               + Thread.currentThread().getName() + " begin time="

               + System.currentTimeMillis());

         Thread.sleep(5000);

         System.out.println("end");

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

   }



}

 

 

public class ThreadA extends Thread {



   private MyObject object;



   public ThreadA(MyObject object) {

      super();

      this.object = object;

   }



   @Override

   public void run() {

      super.run();

      object.methodA();

   }



}

 


public class ThreadB extends Thread {



   private MyObject object;



   public ThreadB(MyObject object) {

      super();

      this.object = object;

   }



   @Override

   public void run() {

      super.run();

      object.methodB();

   }

}

 


public class Run {



   public static void main(String[] args) {

      MyObject object = new MyObject();

      ThreadA a = new ThreadA(object);

      a.setName("A");

      ThreadB b = new ThreadB(object);

      b.setName("B");



      a.start();

      b.start();

   }



}

 

运行结果为:

begin methodA threadName=A

begin methodB threadName=B begin time=1533711232153

end

end endTime=1533711237153

 

由此我们可以得出结论:

A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中非synchronized类型的方法。

A线程先持有object对象的Lock锁,B线程如果调用object对象中synchronized类型的方法则需要等待,也就是同步。

 

 

synchronized锁重入

         关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的,在一个synchronized方法的内部调用本类的其他synchronized方法时,是永远可以得到锁的。

public class MyThread extends Thread {

   @Override

   public void run() {

      Service service = new Service();

      service.service1();

   }



}
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");

   }



}

 


public class Run {

   public static void main(String[] args) {

      MyThread t = new MyThread();

      t.start();

   }

}

 

运行结果:

service1

service2

service3

可重入锁:在获取到该对象的一个锁并且没有释放,可以再次获取自己的内部锁。

并且子类完全可以通过“可重入锁”调用父类的同步方法。

 

其他

  1. 出现异常,自动释放锁
  2. 同步不能继承。

 

Synchronized同步代码块

 

Synchronized同步方法的弊端:效果过低,使用Synchronized同步方法,会导致整个方法同步,部分不必要同步的代码段也会同步。A线程调用同步方法执行一个长时间的任务,那B线程就需要等待较长时间,这种情况得用synchronized同步语块来解决。

使用synchronized同步代码块

当一个线程访问Object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized同步代码块。因此可以说不在synchronized中就是异步执行,在synchronized块中就是同步执行。实现一半同步一半异步。

直接看代码:

public class Task {



   public void doLongTimeTask() throws InterruptedException {

      for (int i = 0; i < 10; i++) {

         System.out.println("nosynchronized threadName="

               + Thread.currentThread().getName() + " i=" + (i + 1));

      }

      System.out.println("");

      synchronized (this) {

         for (int i = 0; i < 10; i++) {

            System.out.println("synchronized threadName="

                  + Thread.currentThread().getName() + " i=" + (i + 1));

         }

      }



   }

}

 

public class MyThread1 extends Thread {



   private Task task;



   public MyThread1(Task task) {

      super();

      this.task = task;

   }



   @Override

   public void run() {

      super.run();

      try {

         task.doLongTimeTask();

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

   }



}

 

 
public class MyThread2 extends Thread {



   private Task task;



   public MyThread2(Task task) {

      super();

      this.task = task;

   }



   @Override

   public void run() {

      super.run();

      try {

         task.doLongTimeTask();

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

   }



}

 

public class Run {



   public static void main(String[] args) {

      Task task = new Task();



      MyThread1 thread1 = new MyThread1(task);

      thread1.start();



      MyThread2 thread2 = new MyThread2(task);

      thread2.start();

   }

}

 

运行结果为:

nosynchronized threadName=Thread-0 i=1

nosynchronized threadName=Thread-1 i=1

nosynchronized threadName=Thread-0 i=2

nosynchronized threadName=Thread-1 i=2

nosynchronized threadName=Thread-0 i=3

nosynchronized threadName=Thread-1 i=3

nosynchronized threadName=Thread-0 i=4

nosynchronized threadName=Thread-1 i=4

nosynchronized threadName=Thread-0 i=5

nosynchronized threadName=Thread-1 i=5

nosynchronized threadName=Thread-0 i=6

nosynchronized threadName=Thread-1 i=6

nosynchronized threadName=Thread-0 i=7

nosynchronized threadName=Thread-1 i=7

nosynchronized threadName=Thread-0 i=8

nosynchronized threadName=Thread-1 i=8

nosynchronized threadName=Thread-0 i=9

nosynchronized threadName=Thread-1 i=9

nosynchronized threadName=Thread-0 i=10

nosynchronized threadName=Thread-1 i=10

 

 

synchronized threadName=Thread-1 i=1

synchronized threadName=Thread-1 i=2

synchronized threadName=Thread-1 i=3

synchronized threadName=Thread-1 i=4

synchronized threadName=Thread-1 i=5

synchronized threadName=Thread-1 i=6

synchronized threadName=Thread-1 i=7

synchronized threadName=Thread-1 i=8

synchronized threadName=Thread-1 i=9

synchronized threadName=Thread-1 i=10

synchronized threadName=Thread-0 i=1

synchronized threadName=Thread-0 i=2

synchronized threadName=Thread-0 i=3

synchronized threadName=Thread-0 i=4

synchronized threadName=Thread-0 i=5

synchronized threadName=Thread-0 i=6

synchronized threadName=Thread-0 i=7

synchronized threadName=Thread-0 i=8

synchronized threadName=Thread-0 i=9

synchronized threadName=Thread-0 i=10

 

非同步代码块是异步执行, 而同步代码块是同步执行,所以是一半同步一半异步。

 

 

同步代码块synchronized(this) 锁定的也是当前对象,根据此特定,说明在一个类中,有多个synchronized代码块的情况下,当一个线程访问的一个synchronized同步代码块时,其他线程对同一个object中所有其他synchronized(this)的访问都将被阻塞,说明一个类里,synchronized的对象监视器是同一个。

验证代码:

 

public class ThreadA extends Thread {



   private ObjectService service;



   public ThreadA(ObjectService service) {

      super();

      this.service = service;

   }



   @Override

   public void run() {

      super.run();

      service.serviceMethodA();

   }



}

 

 

public class ThreadB extends Thread {

   private ObjectService service;



   public ThreadB(ObjectService service) {

      super();

      this.service = service;

   }



   @Override

   public void run() {

      super.run();

      service.serviceMethodB();

   }

}

 

public class ObjectService {



   public void serviceMethodA() {

      try {

         synchronized (this) {

            System.out.println("A begin time=" + System.currentTimeMillis());

            Thread.sleep(2000);

            System.out.println("A end    end=" + System.currentTimeMillis());

         }

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

   }
 
public class Run {



   public static void main(String[] args) {

      ObjectService service = new ObjectService();



      ThreadA a = new ThreadA(service);

      a.setName("a");

      a.start();



      ThreadB b = new ThreadB(service);

      b.setName("b");

      b.start();

   }



}
   public void serviceMethodB() {

      synchronized (this) {

         System.out.println("B begin time=" + System.currentTimeMillis());

         System.out.println("B end    end=" + System.currentTimeMillis());

      }

   }

}

 

运行结果:A begin time=1533714188830

A end    end=1533714190830

B begin time=1533714190830

B end    end=1533714190830

 

 

将任意对象作为对象监视器而非this

                   synchronized同步方法:

                   1对其他synchronized同步方法或者synchronized(this)同步代码块调用呈阻塞状态。

                   2同一时间只有一个线程可以执行synchronized同步方法中的代码

                   synchronized(this)同步代码块:

                   1对其他synchronized同步方法或者synchronized(this)同步代码块调用呈阻塞状态。

                   2同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码

 

锁非this对象具有一定的优点:如果一个类中有多个synchronized方法,这是虽然能实现同步,但是会收到阻塞,所以影响运行效率;但是如果使用同步代码块锁非this对象(这里是关键,是关于synchronized代码块和synchronized方法的阻塞情况),synchronized(非this)代码块中的程序与同步方法是异步的,不会与其他锁this同步犯法争抢this锁,可以提高效率。

上代码:

 

public class ThreadA extends Thread {

   private Service service;



   public ThreadA(Service service) {

      super();

      this.service = service;

   }



   @Override

   public void run() {

      service.a();



   }



}

 

 

public class ThreadB extends Thread {



   private Service service;



   public ThreadB(Service service) {

      super();

      this.service = service;

   }



   @Override

   public void run() {

      service.b();



   }



}

 

public class Service {



   private String anyString = new String();



   public void a() {

      try {

         synchronized (anyString) {

            System.out.println("a begin");

            Thread.sleep(3000);

            System.out.println("a   end");

         }

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

   }



   synchronized public void b() {

      System.out.println("b begin");

      System.out.println("b   end");

   }



}

 

public class Run {



   public static void main(String[] args) {

      Service service = new Service();



      ThreadA a = new ThreadA(service);

      a.setName("A");

      a.start();



      ThreadB b = new ThreadB(service);

      b.setName("B");

      b.start();



   }



}

 

运行结果为异步:

a begin

b begin

b   end

a   end

--------------------------------------------over------------------------------------------------

猜你喜欢

转载自blog.csdn.net/u012133048/article/details/81510833