Java多线程同步与synchronized

转载 2015年11月20日 20:23:14

为什么需要同步多线程?

线程的同步是指让多个运行的线程在一起良好地协作,达到让多线程按要求合理地占用释放资源。我们采用Java中的同步代码块和同步方法达到这样的目的。比如这样的解决多线程无固定序执行的问题:
public class TwoThreadTest {
        public static void main(String[] args) {
              Thread th1= new MyThread1();
              Thread th2= new MyThread2();
              th1.start();
              th2.start();
       }
}
class MyThread2 extends Thread{
        @Override
        public void run() {
               for( int i=0;i<10;i++)
                     System. out.println( "thread 1 counter:"+i);
       }
}
class MyThread1 extends Thread{
        @Override
        public void run() {
               for( int i=0;i<10;i++)
                     System. out.println( "thread 2 counter:"+i);
       }      
}
这种状态下多线程执行的结果是随机地去任意插入执行,这完全取决于JVM对于线程的调度,在很多要求定序执行的情况下,这种随机执行的状态显然是不合要求的。
public class ThreadTest {
        public static void main(String[] args) {
              MyThread thread = new MyThread();
              Thread th1= new Thread(thread);
              Thread th2= new Thread(thread);
              th1.start();
              th2.start();
       }
}
class MyThread implements Runnable{
        @Override
        public synchronized void run() {
               for( int i=0;i<10;i++)
                     System. out.println(Thread. currentThread().getName()+" counter:"+i);
       }
}
运行结果:
Thread-0 counter:0
Thread-0 counter:1
Thread-0 counter:2
Thread-0 counter:3
Thread-0 counter:4
Thread-0 counter:5
Thread-0 counter:6
Thread-0 counter:7
Thread-0 counter:8
Thread-0 counter:9
Thread-1 counter:0
Thread-1 counter:1
Thread-1 counter:2
Thread-1 counter:3
Thread-1 counter:4
Thread-1 counter:5
Thread-1 counter:6
Thread-1 counter:7
Thread-1 counter:8
Thread-1 counter:9

使用了同步方法后我们就可以控制线程独占执行体对象,这样在执行的过程中就可以使得线程将执行体上的任务一次性执行完后退出锁定状态,JVM再调度另一个线程进来一次性运行执行体内的任务。

线程创建运行的范式

在以前我们也有自己的线程创建和运行的编程范式,一般是定义一个执行类重写run()方法,但是这种方式将执行体和执行的任务放在了一起,从软件工程的角度来看不利于解耦。一个线程的执行的意思是说线程通过执行对象执行了某个对象的某个任务,从这个角度来说,将任务的规定者从执行类中分离出来可以使得多线程编程的各个角色明晰出来,进而获得良好地解耦,以下就是线程创建和执行的编程范式:
public class FormalThreadClass {
        public static void main(String[] args) {
              Thread thread = new Thread( new MyRunnable());
              thread.start();
       }
}
class MyRunnable implements Runnable{
       MyTask myTask = new MyTask();
        @Override
        public void run() {
               myTask.doTask();
       }
}
class MyTask{
        public void doTask() {
              System. out.println( "This is real Tasking");
       }
}

synchronized关键字

synchronized可以用来修饰方法以构成同步方法,还可以修饰对象构成同步代码块,最终的目的都是一样的:
给要访问数据的线程添加一个规定:一次只允许一个线程访问数据。只有?当前正在访问数据”的线程结束访问之后,其他线程才允许访问这个数据。

关于synchronized关键字,有以下几点来说明:

  1. 当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

  2. 当两个并发线程访问同一个对象object中的这个synchronized同步代码块或同步方法时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块或同步方法以后才能执行该代码块或同步方法。

  3. 然而,当一个线程访问object的一个synchronized同步代码块或同步方法时,另一个线程仍然可以访问该object中的非synchronized同步代码块或非synchronized同步方法。

  4. 尤其关键的是,当一个线程访问object的一个synchronized同步代码块或同步方法时,其他线程对object中所有其它synchronized同步代码块或同步方法的访问将被阻塞。

1.以下这个例子可以说明synchronized方法的这些特性,同步代码块也是一样:

① synchronized方法表面上它只是锁定了当前的方法本身,实际上当synchronized方法起作用的时候,整个对象的带有synchronized的方法都将被锁定,这也就是为什么当一个线程执行一个synchronized方法时,其他的线程除了不能访问当前的同步方法外还并不能访问其他的同步方法,而只能访问非synchronized方法,因为这种锁定是对象级别的。

public class ThreadTest {
        public static void main(String[] args) {
               final MyTask myTask = new MyTask();
              Thread thread1 = new Thread( new Runnable() {
                      public void run() {
                           myTask.doTask1();
                     }
              });
              Thread thread2 = new Thread( new Runnable() {
                      public void run() {
                           myTask.doTask2();
                     }
              });
              thread1.start();
              thread2.start();
       }
}
class MyTask{
        public synchronized void doTask1() {
               for ( int i = 0; i < 5; i++) {
                     System. out.println( "1 This is real Tasking "+i);
              }
       }
        public void doTask2() {
               for ( int i = 0; i < 5; i++) {
                     System. out.println( "2 This is real Tasking "+i);
              }
       }
}
运行结果:
1 This is real Tasking 0
2 This is real Tasking 0
1 This is real Tasking 1
2 This is real Tasking 1
2 This is real Tasking 2
1 This is real Tasking 2
2 This is real Tasking 3
2 This is real Tasking 4
1 This is real Tasking 3
1 This is real Tasking 4
② 如使在静态方法中用synchronized时,因为这个方法就不是仅属于某个对象而是属于整个类的了,所以一旦一个线程进入了这个代码块就会将这个类的所有对象的所有synchronized方法或synchronized同步代码块锁定,其他的线程就没有办法访问所有这些对象的synchronized方法和synchronized代码块(注意其他线程还是仍然能访问这些对象的非synchronized方法和synchronized代码块的),因此这种锁定是class级别的。
public class FormalThreadClass {
        public static void main(String[] args) {
              MyTask myTask1 = new MyTask();
              MyTask myTask2 = new MyTask();
              Thread thread1 = new Thread( new MyRunnable(myTask1));
              Thread thread2 = new Thread( new MyRunnable(myTask2));
              thread1.start();
              thread2.start();
       }
}
class MyRunnable implements Runnable {
       MyTask myTask;
        public MyRunnable(MyTask myTask) {
               this. myTask = myTask;
       }
        @Override
        public void run() {
              MyTask. doTask();
       }
}
class MyTask {
        public static synchronized void doTask() {
               for ( int i = 0; i < 5; i++) {
                     System. out.println(Thread. currentThread().getName()+" running "+i);
              }
       }
}
运行结果:
Thread-1 running 0
Thread-1 running 1
Thread-1 running 2
Thread-1 running 3
Thread-1 running 4
Thread-0 running 0
Thread-0 running 1
Thread-0 running 2
Thread-0 running 3
Thread-0 running 4

2.synchronized同步代码块是对一个对象作为参数进行锁定。

① 如在使用synchronized(this)时,一旦一个线程进入了这个代码块就会将整个对象的所有synchronized方法或synchronized同步代码块锁定,其他的线程就没有办法访问这个对象的synchronized方法和synchronized代码块(注意其他线程还是仍然能访问这个对象的非synchronized方法和synchronized代码块的)。

public class ThreadTest {
        public static void main(String[] args) {
               final MyTask myTask = new MyTask();
              Thread thread1 = new Thread( new Runnable() {
                      public void run() {
                           myTask.doTask1();
                     }
              });
              Thread thread2 = new Thread( new Runnable() {
                      public void run() {
                           myTask.doTask2();
                     }
              });
              thread1.start();
              thread2.start();
       }
}
class MyTask {
        public void doTask1() {
               synchronized (this) {
                      for ( int i = 0; i < 5; i++) {
                           System. out.println( "1 is running");
                     }
              }
       }
        public void doTask2() {
               for ( int i = 0; i < 5; i++) {
                     System. out.println( "2 is running");
              }
       }
}
运行结果:
1 This is real Tasking 0
2 This is real Tasking 0
1 This is real Tasking 1
2 This is real Tasking 1
2 This is real Tasking 2
2 This is real Tasking 3
2 This is real Tasking 4
1 This is real Tasking 2
1 This is real Tasking 3
1 This is real Tasking 4
所以:synchronized方法实际上等同于用一个synchronized块包住方法中的所有语句,然后在synchronized块的括号中传入this关键字。当然,如果是静态方法,需要锁定的则是class对象。

② 如在使用synchronized(.class)时,一旦一个线程进入了这个代码块就会将整个类的所有这个synchronized(.class) 同步代码块锁定,其他的线程就没有办法访问这个对象的synchronized(**.class) 代码块,这种锁也是class级别的,但要注意在这种情况下,其他线程仍然是可以访问仅做了synchronized的代码块或非静态方法的,因为它们仅仅是对当前对象的锁定。
public class FormalThreadClass {
        public static void main(String[] args) {
              MyTask myTask1 = new MyTask();
              MyTask myTask2 = new MyTask();
              Thread thread1 = new Thread( new MyRunnable(myTask1));
              Thread thread2 = new Thread( new MyRunnable(myTask2));
              thread1.start();
              thread2.start();
       }
}
class MyRunnable implements Runnable {
       MyTask myTask;
        public MyRunnable(MyTask myTask) {
               this. myTask = myTask;
       }
        @Override
        public void run() {
               myTask.doTask();
       }
}
class MyTask {
        public  void doTask() {
               synchronized (MyTask.class ) {
                      for ( int i = 0; i < 5; i++) {
                           System. out.println(Thread. currentThread().getName()+" running "+i);
                     }
              }
       }
}
运行结果:
Thread-0 running 0
Thread-0 running 1
Thread-0 running 2
Thread-0 running 3
Thread-0 running 4
Thread-1 running 0
Thread-1 running 1
Thread-1 running 2
Thread-1 running 3
Thread-1 running 4

总结起来这一部分:

synchronized方法是一种粗粒度的并发控制手段,某一时刻只能有一个线程执行该方法。synchroized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内synchroized块之外的代码是可以被多个线程同时访问到。


转载自:http://segmentfault.com/a/1190000003810166

相关文章推荐

使用synchronized和volatile实现Java多线程同步

上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程...

java多线程同步 synchronized

注:本文内容参考自网络,源头以无法追溯,但是纯手工总结敲打。如有不准确之处,还请各位指教。多线程同步概述提到多线程就不得不提共享资源。当有多个线程去访问同一个共享资源时,会造成错误。比如比较常见的:同...

java线程多线程同步机制(synchronized)

java的线程一直是一个让初学者一个头疼不已的问题,反正是当初可能学习的时候比较烦躁,线程这一部分一直没有了解过他的具体概念和用处,参加工作后才学习的这一块相关的知识,呵呵,下面不多少废话了,说正题。...

Java多线程同步Synchronized使用分析

本文转载于http://www.cnblogs.com/tt_mc/archive/2012/02/28/2372607.html 同步的概念: 同步分为 同步方法 和 同步块 两种方式。 锁定...
  • wainixy
  • wainixy
  • 2012年08月31日 20:28
  • 115

Java多线程同步总结之synchronized

线程安全 线程安全,是指每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该...

Java多线程同步Synchronized深入解析 类对象和类的实例对象

      同步的概念: 同步分为 同步方法 和 同步块 两种方式。 锁定的内容分为 锁定类的某个特定实例 和 锁定类对象(类的所有实例) 变量分为 实例变量(不...

Java多线程同步Synchronized使用分析

Synchronized 锁定的是 类变量 ,即static 变量(可能是属性,可能是方法)(锁定类对象) AD: 由于servlet 是多线程单例的。 struts1 的前端控制器...
  • novelly
  • novelly
  • 2012年02月13日 15:35
  • 279

Java 多线程同步 锁机制与synchronized

转自:http://www.cnblogs.com/psjay/archive/2010/04/01/1702465.html(感谢原作者!!) Java 多线程同步 锁机制与synchroni...
  • bnuside
  • bnuside
  • 2011年11月01日 12:50
  • 504

Java 多线程同步 锁机制与synchronized

打个比方:一个object就像一个大房子,大门永远打开。房子里有很多房间(也就是方法)。这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把...

Java多线程同步机制(synchronized)

转至 : http://enetor.iteye.com/blog/986623 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 Java里边就是拿到某...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java多线程同步与synchronized
举报原因:
原因补充:

(最多只允许输入30个字)