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 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) {

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

   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 void serviceMethodB() {

      synchronized (this) {

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

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

      }

   }

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

   }



}

 

运行结果: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------------------------------------------------

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值