Java多线程编程中synchronized线程同步的教程

0.关于线程同步
(1)为什么需要同步多线程?
线程的同步是指让多个运行的线程在一起良好地协作,达到让多线程按要求合理地占用释放资源。我们采用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);
  }
}

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

(2)线程创建运行的范式:
在以前我们也有自己的线程创建和运行的编程范式,一般是定义一个执行类重写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");
  }
}

1. synchronized原理
在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。
当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。

2. synchronized基本规则
我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。
第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
(1)第一条:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。 下面是“synchronized代码块”对应的演示程序。

class MyRunable implements Runnable {

 @Override
 public void run() {
  synchronized(this) {
   try {
    for (int i = 0; i < 5; i++) {
     Thread.sleep(100); // 休眠100ms
     System.out.println(Thread.currentThread().getName() + " loop " + i);
    }
   } catch (InterruptedException ie) {
   }
  }
 }
}

public class Demo1_1 {

 public static void main(String[] args) {
  Runnable demo = new MyRunable();  // 新建“Runnable对象”

  Thread t1 = new Thread(demo, "t1"); // 新建“线程t1”, t1是基于demo这个Runnable对象
  Thread t2 = new Thread(demo, "t2"); // 新建“线程t2”, t2是基于demo这个Runnable对象
  t1.start();       // 启动“线程t1”
  t2.start();       // 启动“线程t2”
 }
}

运行结果:

t1 loop 0
t1 loop 1
t1 loop 2
t1 loop 3
t1 loop 4
t2 loop 0
t2 loop 1
t2 loop 2
t2 loop 3
t2 loop 4

结果说明:run()方法中存在“synchronized(this)代码块”,而且t1和t2都是基于"demo这个Runnable对象"创建的线程。这就意味着,我们可以将synchronized(this)中的this看作是“demo这个Runnable对象”;因此,线程t1和t2共享“demo对象的同步锁”。所以,当一个线程运行的时候,另外一个线程必须等待“运行线程”释放“demo的同步锁”之后才能运行。
如果你确认,你搞清楚这个问题了。那我们将上面的代码进行修改,然后再运行看看结果怎么样,看看你是否会迷糊。修改后的源码如下:

class MyThread extends Thread {

 public MyThread(String name) {
  super(name);
 }

 @Override
 public void run() {
  synchronized(this) {
   try {
    for (int i = 0; i < 5; i++) {
     Thread.sleep(100); // 休眠100ms
     System.out.println(Thread.currentThread().getName() + " loop " + i);
    }
   } catch (InterruptedException ie) {
   }
  }
 }
}

public class Demo1_2 {

 public static void main(String[] args) {
  Thread t1 = new MyThread("t1"); // 新建“线程t1”
  Thread t2 = new MyThread("t2"); // 新建“线程t2”
  t1.start();       // 启动“线程t1”
  t2.start();       // 启动“线程t2”
 }
}

代码说明:比较Demo1_2 和 Demo1_1,我们发现,Demo1_2中的MyThread类是直接继承于Thread,而且t1和t2都是MyThread子线程。
幸运的是,在“Demo1_2的run()方法”也调用了synchronized(this),正如“Demo1_1的run()方法”也调用了synchronized(this)一样!
那么,Demo1_2的执行流程是不是和Demo1_1一样呢?运行结果:

t1 loop 0
t2 loop 0
t1 loop 1
t2 loop 1
t1 loop 2
t2 loop 2
t1 loop 3
t2 loop 3
t1 loop 4
t2 loop 4

结果说明:
如果这个结果一点也不令你感到惊讶,那么我相信你对synchronized和this的认识已经比较深刻了。否则的话,请继续阅读这里的分析。
synchronized(this)中的this是指“当前的类对象”,即synchronized(this)所在的类对应的当前对象。它的作用是获取“当前对象的同步锁”。
对于Demo1_2中,synchronized(this)中的this代表的是MyThread对象,而t1和t2是两个不同的MyThread对象,因此t1和t2在执行synchronized(this)时,获取的是不同对象的同步锁。对于Demo1_1对而言,synchronized(this)中的this代表的是MyRunable对象;t1和t2共同一个MyRunable对象,因此,一个线程获取了对象的同步锁,会造成另外一个线程等待。
(2)第二条:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
下面是“synchronized代码块”对应的演示程序。

class Count {

 // 含有synchronized同步块的方法
 public void synMethod() {
  synchronized(this) {
   try {
    for (int i = 0; i < 5; i++) {
     Thread.sleep(100); // 休眠100ms
     System.out.println(Thread.currentThread().getName() + " synMethod loop " + i);
    }
   } catch (InterruptedException ie) {
   }
  }
 }

 // 非同步的方法
 public void nonSynMethod() {
  try {
   for (int i = 0; i < 5; i++) {
    Thread.sleep(100);
    System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i);
   }
  } catch (InterruptedException ie) {
  }
 }
}

public class Demo2 {

 public static void main(String[] args) {
  final Count count = new Count();
  // 新建t1, t1会调用“count对象”的synMethod()方法
  Thread t1 = new Thread(
    new Runnable() {
     @Override
     public void run() {
      count.synMethod();
     }
    }, "t1");

  // 新建t2, t2会调用“count对象”的nonSynMethod()方法
  Thread t2 = new Thread(
    new Runnable() {
     @Override
     public void run() {
      count.nonSynMethod();
     }
    }, "t2"); 

  t1.start(); // 启动t1
  t2.start(); // 启动t2
 }
}

运行结果:

t1 synMethod loop 0
t2 nonSynMethod loop 0
t1 synMethod loop 1
t2 nonSynMethod loop 1
t1 synMethod loop 2
t2 nonSynMethod loop 2
t1 synMethod loop 3
t2 nonSynMethod loop 3
t1 synMethod loop 4
t2 nonSynMethod loop 4

结果说明:
主线程中新建了两个子线程t1和t2。t1会调用count对象的synMethod()方法,该方法内含有同步块;而t2则会调用count对象的nonSynMethod()方法,该方法不是同步方法。t1运行时,虽然调用synchronized(this)获取“count的同步锁”;但是并没有造成t2的阻塞,因为t2没有用到“count”同步锁。
(3)第三条:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
我们将上面的例子中的nonSynMethod()方法体的也用synchronized(this)修饰。修改后的源码如下:

class Count {

 // 含有synchronized同步块的方法
 public void synMethod() {
  synchronized(this) {
   try {
    for (int i = 0; i < 5; i++) {
     Thread.sleep(100); // 休眠100ms
     System.out.println(Thread.currentThread().getName() + " synMethod loop " + i);
    }
   } catch (InterruptedException ie) {
   }
  }
 }

 // 也包含synchronized同步块的方法
 public void nonSynMethod() {
  synchronized(this) {
   try {
    for (int i = 0; i < 5; i++) {
     Thread.sleep(100);
     System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i);
    }
   } catch (InterruptedException ie) {
   }
  }
 }
}

public class Demo3 {

 public static void main(String[] args) {
  final Count count = new Count();
  // 新建t1, t1会调用“count对象”的synMethod()方法
  Thread t1 = new Thread(
    new Runnable() {
     @Override
     public void run() {
      count.synMethod();
     }
    }, "t1");

  // 新建t2, t2会调用“count对象”的nonSynMethod()方法
  Thread t2 = new Thread(
    new Runnable() {
     @Override
     public void run() {
      count.nonSynMethod();
     }
    }, "t2"); 

  t1.start(); // 启动t1
  t2.start(); // 启动t2
 }
}

运行结果:

t1 synMethod loop 0
t1 synMethod loop 1
t1 synMethod loop 2
t1 synMethod loop 3
t1 synMethod loop 4
t2 nonSynMethod loop 0
t2 nonSynMethod loop 1
t2 nonSynMethod loop 2
t2 nonSynMethod loop 3
t2 nonSynMethod loop 4

结果说明:
主线程中新建了两个子线程t1和t2。t1和t2运行时都调用synchronized(this),这个this是Count对象(count),而t1和t2共用count。因此,在t1运行时,t2会被阻塞,等待t1运行释放“count对象的同步锁”,t2才能运行。

3. synchronized方法 和 synchronized代码块
“synchronized方法”是用synchronized修饰方法,而 “synchronized代码块”则是用synchronized修饰代码块。
synchronized方法示例

public synchronized void foo1() {
  System.out.println("synchronized methoed");
}
synchronized代码块
public void foo2() {
  synchronized (this) {
    System.out.println("synchronized methoed");
  }
}

synchronized代码块中的this是指当前对象。也可以将this替换成其他对象,例如将this替换成obj,则foo2()在执行synchronized(obj)时就获取的是obj的同步锁。
synchronized代码块可以更精确的控制冲突限制访问区域,有时候表现更高效率。下面通过一个示例来演示:

// Demo4.java的源码
public class Demo4 {

  public synchronized void synMethod() {
    for(int i=0; i<1000000; i++)
      ;
  }

  public void synBlock() {
    synchronized( this ) {
      for(int i=0; i<1000000; i++)
        ;
    }
  }

  public static void main(String[] args) {
    Demo4 demo = new Demo4();

    long start, diff;
    start = System.currentTimeMillis();        // 获取当前时间(millis)
    demo.synMethod();                // 调用“synchronized方法”
    diff = System.currentTimeMillis() - start;    // 获取“时间差值”
    System.out.println("synMethod() : "+ diff);

    start = System.currentTimeMillis();        // 获取当前时间(millis)
    demo.synBlock();                // 调用“synchronized方法块”
    diff = System.currentTimeMillis() - start;    // 获取“时间差值”
    System.out.println("synBlock() : "+ diff);
  }
}

(某一次)执行结果:

synMethod() : 11
synBlock() : 3

4. 实例锁 和 全局锁
实例锁 -- 锁在某一个实例对象上。如果该类是单例,那么该锁也具有全局锁的概念。
(1)实例锁对应的就是synchronized关键字。
(2)全局锁 -- 该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。
全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。
关于“实例锁”和“全局锁”有一个很形象的例子:

pulbic class Something {
  public synchronized void isSyncA(){}
  public synchronized void isSyncB(){}
  public static synchronized void cSyncA(){}
  public static synchronized void cSyncB(){}
}

假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。
(1) x.isSyncA()与x.isSyncB()
(2) x.isSyncA()与y.isSyncA()
(3) x.cSyncA()与y.cSyncB()
(4) x.isSyncA()与Something.cSyncA()

(1) 不能被同时访问。
因为isSyncA()和isSyncB()都是访问同一个对象(对象x)的同步锁!

// LockTest1.java的源码
class Something {
  public synchronized void isSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public synchronized void isSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
}

public class LockTest1 {

  Something x = new Something();
  Something y = new Something();

  // 比较(01) x.isSyncA()与x.isSyncB()
  private void test1() {
    // 新建t11, t11会调用 x.isSyncA()
    Thread t11 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            x.isSyncA();
          }
        }, "t11");

    // 新建t12, t12会调用 x.isSyncB()
    Thread t12 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            x.isSyncB();
          }
        }, "t12"); 

    t11.start(); // 启动t11
    t12.start(); // 启动t12
  }

  public static void main(String[] args) {
    LockTest1 demo = new LockTest1();
    demo.test1();
  }
}

运行结果:

t11 : isSyncA
t11 : isSyncA
t11 : isSyncA
t11 : isSyncA
t11 : isSyncA
t12 : isSyncB
t12 : isSyncB
t12 : isSyncB
t12 : isSyncB
t12 : isSyncB

(2) 可以同时被访问

因为访问的不是同一个对象的同步锁,x.isSyncA()访问的是x的同步锁,而y.isSyncA()访问的是y的同步锁。

// LockTest2.java的源码
class Something {
  public synchronized void isSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public synchronized void isSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
  public static synchronized void cSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : cSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public static synchronized void cSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : cSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
}

public class LockTest2 {

  Something x = new Something();
  Something y = new Something();

  // 比较(02) x.isSyncA()与y.isSyncA()
  private void test2() {
    // 新建t21, t21会调用 x.isSyncA()
    Thread t21 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            x.isSyncA();
          }
        }, "t21");

    // 新建t22, t22会调用 x.isSyncB()
    Thread t22 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            y.isSyncA();
          }
        }, "t22"); 

    t21.start(); // 启动t21
    t22.start(); // 启动t22
  }

  public static void main(String[] args) {
    LockTest2 demo = new LockTest2();

    demo.test2();
  }
}

运行结果:

t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA

(3) 不能被同时访问
因为cSyncA()和cSyncB()都是static类型,x.cSyncA()相当于Something.isSyncA(),y.cSyncB()相当于Something.isSyncB(),因此它们共用一个同步锁,不能被同时反问。

// LockTest3.java的源码
class Something {
  public synchronized void isSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public synchronized void isSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
  public static synchronized void cSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : cSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public static synchronized void cSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : cSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
}

public class LockTest3 {

  Something x = new Something();
  Something y = new Something();

  // 比较(03) x.cSyncA()与y.cSyncB()
  private void test3() {
    // 新建t31, t31会调用 x.isSyncA()
    Thread t31 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            x.cSyncA();
          }
        }, "t31");

    // 新建t32, t32会调用 x.isSyncB()
    Thread t32 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            y.cSyncB();
          }
        }, "t32"); 

    t31.start(); // 启动t31
    t32.start(); // 启动t32
  }

  public static void main(String[] args) {
    LockTest3 demo = new LockTest3();

    demo.test3();
  }
}

运行结果:

t31 : cSyncA
t31 : cSyncA
t31 : cSyncA
t31 : cSyncA
t31 : cSyncA
t32 : cSyncB
t32 : cSyncB
t32 : cSyncB
t32 : cSyncB
t32 : cSyncB

(4) 可以被同时访问
因为isSyncA()是实例方法,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是“类的锁”。因此,它们是可以被同时访问的。

// LockTest4.java的源码
class Something {
  public synchronized void isSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public synchronized void isSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : isSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
  public static synchronized void cSyncA(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : cSyncA");
      }
    }catch (InterruptedException ie) {
    }
  }
  public static synchronized void cSyncB(){
    try {
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); // 休眠100ms
        System.out.println(Thread.currentThread().getName()+" : cSyncB");
      }
    }catch (InterruptedException ie) {
    }
  }
}

public class LockTest4 {

  Something x = new Something();
  Something y = new Something();

  // 比较(04) x.isSyncA()与Something.cSyncA()
  private void test4() {
    // 新建t41, t41会调用 x.isSyncA()
    Thread t41 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            x.isSyncA();
          }
        }, "t41");

    // 新建t42, t42会调用 x.isSyncB()
    Thread t42 = new Thread(
        new Runnable() {
          @Override
          public void run() {
            Something.cSyncA();
          }
        }, "t42"); 

    t41.start(); // 启动t41
    t42.start(); // 启动t42
  }

  public static void main(String[] args) {
    LockTest4 demo = new LockTest4();

    demo.test4();
  }
}

运行结果:

t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
(0)

相关推荐

  • Java多线程编程中synchronized关键字的基础用法讲解

    多线程编程中,最关键.最关心的问题应该就是同步问题,这是一个难点,也是核心. 从jdk最早的版本的synchronized.volatile,到jdk 1.5中提供的java.util.concurrent.locks包中的Lock接口(实现有ReadLock,WriteLock,ReentrantLock),多线程的实现也是一步步走向成熟化.   同步,它是通过什么机制来控制的呢?第一反应就是锁,这个在学习操作系统与数据库的时候,应该都已经接触到了.在Java的多线程程序中,当多个程序竞争同一

  • 解析Java编程之Synchronized锁住的对象

    图片上传 密码修改为  synchronized是java中用于同步的关键字,一般我们通过Synchronized锁住一个对象,来进行线程同步.我们需要了解在程序执行过程中,synchronized锁住的到底是哪个对象,否则我们在多线程的程序就有可能出现问题. 看下面的代码,我们定义了一个静态变量n,在run方法中,我们使n增加10,然后在main方法中,我们开辟了100个线程,来执行n增加的操作,如果线程没有并发执行,那么n最后的值应该为1000,显然下面的程序执行完结果不是1000,因为我们

  • Java中synchronized关键字修饰方法同步的用法详解

    Java的最基本的同步方式,即使用synchronized关键字来控制一个方法的并发访问. 每一个用synchronized关键字声明的方法都是临界区.在Java中,同一个对象的临界区,在同一时间只有一个允许被访问. 静态方法则有不同的行为.用synchronized关键字声明的静态方法,同时只能够被一个执行线程访问,但是其他线程可以访问这个对象的非静态的synchronized方法.必须非常谨慎这一点,因为两个线程可以同时访问一个对象的两个不同的synchronized方法,即其中一个是静态s

  • java synchronized同步静态方法和同步非静态方法的异同

    java synchronized 详解 synchronized关键字有两种用法,一种是只用于方法的定义中,另外一种是synchronized块,我们不仅可以使用synchronized来同步一个对象变量,你也可以通synchronizedl来同步类中的静态方法和非静态方法. synchronized块的语法如下: public void method() { synchronized(表达式) { } } public void method() { synchronized(表达式) {

  • 实例解析Java中的synchronized关键字与线程安全问题

    首先来回顾一下synchronized的基本使用: synchronized代码块,被修饰的代码成为同步语句块,其作用的范围是调用这个代码块的对象,我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步.这叫减小锁的粒度,使代码更大程度的并发. synchronized方法,被修饰的方法成为同步方法,其作用范围是整个方法,作用对象是调用这个方法的对象. synchronized静态方法,修饰一个static静态方法,其作用范围是整个

  • Java多线程编程中synchronized线程同步的教程

    0.关于线程同步 (1)为什么需要同步多线程? 线程的同步是指让多个运行的线程在一起良好地协作,达到让多线程按要求合理地占用释放资源.我们采用Java中的同步代码块和同步方法达到这样的目的.比如这样的解决多线程无固定序执行的问题: public class TwoThreadTest { public static void main(String[] args) { Thread th1= new MyThread1(); Thread th2= new MyThread2(); th1.st

  • 详解Java多线程编程中的线程同步方法

    1.多线程的同步: 1.1.同步机制: 在多线程中,可能有多个线程试图访问一个有限的资源,必须预防这种情况的发生.所以引入了同步机制:在线程使用一个资源时为其加锁,这样其他的线程便不能访问那个资源了,直到解锁后才可以访问. 1.2.共享成员变量的例子: 成员变量与局部变量: 成员变量: 如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作,这多个线程是共享一个成员变量的. 局部变量: 如果一个变量是局部变量,那么多个线程对同一个对象进行操作,每个线程都会有一个该局部变量的拷贝.他们

  • java并发编程JUC CountDownLatch线程同步

    目录 java并发编程JUC CountDownLatch线程同步 1.CountDownLatch是什么? 2.CountDownLatch 如何工作 3.CountDownLatch 代码例子 java并发编程JUC CountDownLatch线程同步 CountDownLatch是一种线程同步辅助工具,它允许一个或多个线程等待其他线程正在执行的一组操作完成.CountDownLatch的概念在java并发编程中非常常见,面试也会经常被问到,所以一定要好好理解掌握. CountDownLa

  • java并发编程包JUC线程同步CyclicBarrier语法示例

    目录 1.创建CyclicBarrier障碍 2.在CyclicBarrier障碍处等待 3.CyclicBarrierAction 4.CyclicBarrier例子 在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.PriorityBlockingQueue.SynchronousQueue.BlockingDeque接口.ConcurrentHashMap

  • Java多线程编程安全退出线程方法介绍

    线程停止 Thread提供了一个stop()方法,但是stop()方法是一个被废弃的方法.为什么stop()方法被废弃而不被使用呢?原因是stop()方法太过于暴力,会强行把执行一半的线程终止.这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下 那我们该使用什么来停止线程呢 Thread.interrupt(),我们可以用他来停止线程,他是安全的,可是使用他的时候并不会真的停止了线程,只是会给线程打上了一个记号,至于这个记号有什么用呢

  • 浅谈Java多线程编程中Boolean常量的同步问题

    在JAVA中通过synchronized语句可以实现多线程并发.使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁.锁机制实现了多个线程安全地对临界资源进行访问.   同步代码写法如下:   代码1: Object obj = new Object(); ... synchronized(obj) { //TODO: 访问临界资源 } JAVA的多线程总是充满陷阱,如果我们用Boolean作为被同步的对象,可能会出现以下两种情况:   一. 以为对一个对象加锁,实际同步的是不同对

  • 详解Java多线程编程中LockSupport类的线程阻塞用法

    LockSupport是用来创建锁和其他同步类的基本线程阻塞原语. LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程,而且park()和unpark()不会遇到"Thread.suspend 和 Thread.resume所可能引发的死锁"问题. 因为park() 和 unpark()有许可的存在:调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性. 基本用法 LockSupport 很类似于二元信号

  • 详解Java多线程编程中CountDownLatch阻塞线程的方法

    直译过来就是倒计数(CountDown)门闩(Latch).倒计数不用说,门闩的意思顾名思义就是阻止前进.在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程. CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. CountDownLatch 的作用和 Thread.join() 方法类似,可用于一组线程和另外一组线程的协作.例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工

  • 举例解析Java多线程编程中需要注意的一些关键点

    1. 同步方法或同步代码块? 您可能偶尔会思考是否要同步化这个方法调用,还是只同步化该方法的线程安全子集.在这些情况下,知道 Java 编译器何时将源代码转化为字节代码会很有用,它处理同步方法和同步代码块的方式完全不同. 当 JVM 执行一个同步方法时,执行中的线程识别该方法的 method_info 结构是否有 ACC_SYNCHRONIZED 标记设置,然后它自动获取对象的锁,调用方法,最后释放锁.如果有异常发生,线程自动释放锁. 另一方面,同步化一个方法块会越过 JVM 对获取对象锁和异常

随机推荐