• java 多线程 synchronized块 同步方法


    synchronized关键字又称同步锁

    当方法执行完后,会自动释放锁,只有一个线程能进入此方法

    看看以下的各种例子对synchronized的详细解释

    1.是否加synchronized关键字的不同

     1 public class ThreadTest {
     2 
     3     public static void main(String[] args) {
     4         Example example = new Example();
     5 
     6         Thread t1 = new Thread1(example);
     7         Thread t2 = new Thread1(example);
     8         t1.start();
     9         t2.start();
    10     }
    11 }
    12 
    13 class Example {
    14     public synchronized void excute() {
    15         for (int i = 0; i < 5; ++i) {
    16             try {
    17                 Thread.sleep(1000);
    18             } catch (InterruptedException e) {
    19                 e.printStackTrace();
    20             }
    21             System.out.println("excute:" + i);
    22         }
    23 
    24     }
    25 
    26 }
    27 
    28 class Thread1 extends Thread {
    29     private Example example;
    30 
    31     public Thread1(Example example) {
    32         this.example = example;
    33     }
    34 
    35     @Override
    36     public void run() {
    37         example.excute();
    38     }
    39 }

    加了synchronized关键字的输出结果如下

    会先输出一组0-4,接着再输出下一组,两个线程顺序执行

    excute:0
    excute:1
    excute:2
    excute:3
    excute:4
    excute:0
    excute:1
    excute:2
    excute:3
    excute:4

    没加synchronized关键字的输出结果如下

    两个线程同时执行excute方法,同时并发的

    excute:0
    excute:0
    excute:1
    excute:1
    excute:2
    excute:2
    excute:3
    excute:3
    excute:4
    excute:4

    2.多个方法的多线程情况

     1 public class ThreadTest {
     2 
     3     public static void main(String[] args) {
     4         Example example = new Example();
     5 
     6         Thread t1 = new Thread1(example);
     7         Thread t2 = new Thread2(example);
     8         t1.start();
     9         t2.start();
    10     }
    11 }
    12 
    13 class Example {
    14     public synchronized void excute() {
    15         for (int i = 0; i < 5; ++i) {
    16             try {
    17                 Thread.sleep(1000);
    18             } catch (InterruptedException e) {
    19                 e.printStackTrace();
    20             }
    21             System.out.println("excute:" + i);
    22         }
    23     }
    24     public synchronized void excute1() {
    25         for (int i = 0; i < 5; ++i) {
    26             try {
    27                 Thread.sleep(1000);
    28             } catch (InterruptedException e) {
    29                 e.printStackTrace();
    30             }
    31             System.out.println("excute1:" + i);
    32         }
    33     }
    34 
    35 }
    36 
    37 class Thread1 extends Thread {
    38     private Example example;
    39 
    40     public Thread1(Example example) {
    41         this.example = example;
    42     }
    43 
    44     @Override
    45     public void run() {
    46         example.excute();
    47     }
    48 }
    49 
    50 class Thread2 extends Thread {
    51     private Example example;
    52 
    53     public Thread2(Example example) {
    54         this.example = example;
    55     }
    56 
    57     @Override
    58     public void run() {
    59         example.excute1();
    60     }
    61 }

    执行结果如下

    同样是顺序执行,执行完一个线程再执行另一个线程

    excute:0
    excute:1
    excute:2
    excute:3
    excute:4
    excute1:0
    excute1:1
    excute1:2
    excute1:3
    excute1:4

    如果去掉synchronized关键字,则两个方法并发执行,并没有相互影响。

    但是如例子程序中所写,即便是两个方法:

    执行结果永远是执行完一个线程的输出再执行另一个线程的。  

    说明:

      如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。

    结论:

      当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。

      Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。

      注意这时候是给对象上锁,如果是不同的对象,则各个对象之间没有限制关系。

      尝试在代码中构造第二个线程对象时传入一个新的Example对象,则两个线程的执行之间没有什么制约关系。

    3.静态的同步方法

    当一个synchronized关键字修饰的方法同时又被static修饰,之前说过,非静态的同步方法会将对象上锁,但是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁

     1 public class ThreadTest {
     2 
     3     public static void main(String[] args) {
     4         Example example = new Example();
     5         Example example2 = new Example();
     6         Thread t1 = new Thread1(example);
     7         Thread t2 = new Thread2(example2);
     8         t1.start();
     9         t2.start();
    10     }
    11 }
    12 
    13 class Example {
    14     public synchronized static void excute() {
    15         for (int i = 0; i < 5; ++i) {
    16             try {
    17                 Thread.sleep(1000);
    18             } catch (InterruptedException e) {
    19                 e.printStackTrace();
    20             }
    21             System.out.println("excute:" + i);
    22         }
    23     }
    24     public synchronized static void excute1() {
    25         for (int i = 0; i < 5; ++i) {
    26             try {
    27                 Thread.sleep(1000);
    28             } catch (InterruptedException e) {
    29                 e.printStackTrace();
    30             }
    31             System.out.println("excute1:" + i);
    32         }
    33     }
    34 
    35 }
    36 
    37 class Thread1 extends Thread {
    38     private Example example;
    39 
    40     public Thread1(Example example) {
    41         this.example = example;
    42     }
    43 
    44     @Override
    45     public void run() {
    46         example.excute();
    47     }
    48 }
    49 
    50 class Thread2 extends Thread {
    51     private Example example;
    52 
    53     public Thread2(Example example) {
    54         this.example = example;
    55     }
    56 
    57     @Override
    58     public void run() {
    59         example.excute1();
    60     }
    61 }

    执行结果如下

    excute:0
    excute:1
    excute:2
    excute:3
    excute:4
    excute1:0
    excute1:1
    excute1:2
    excute1:3
    excute1:4

    如果没有static修饰符,两个线程分别传入不同的对象,则会同时并发执行

    所以如果是静态方法的情况(execute()和execute2()都加上static关键字),即便是向两个线程传入不同的Example对象,这两个线程仍然是互相制约的,必须先执行完一个,再执行下一个。

    结论:

      如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的类所对应的Class对象。Java中,无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,它们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始。

    4.synchronized块

    synchronized(object)

    {      

    }

    表示线程在执行的时候会将object对象上锁。(注意这个对象可以是任意类的对象,也可以使用this关键字)。

    这样就可以自行规定上锁对象。

     1 public class ThreadTest {
     2 
     3     public static void main(String[] args) {
     4         Example example = new Example();
     5         Thread t1 = new Thread1(example);
     6         Thread t2 = new Thread2(example);
     7         t1.start();
     8         t2.start();
     9     }
    10 }
    11 
    12 class Example {
    13     public void excute() {
    14         synchronized (this) {
    15             for (int i = 0; i < 5; ++i) {
    16                 try {
    17                     Thread.sleep(1000);
    18                 } catch (InterruptedException e) {
    19                     e.printStackTrace();
    20                 }
    21                 System.out.println("excute:" + i);
    22             }
    23         }
    24         
    25     }
    26     public void excute1() {
    27         synchronized (this) {
    28             for (int i = 0; i < 5; ++i) {
    29                 try {
    30                     Thread.sleep(1000);
    31                 } catch (InterruptedException e) {
    32                     e.printStackTrace();
    33                 }
    34                 System.out.println("excute1:" + i);
    35             }
    36         }
    37         
    38     }
    39 
    40 }
    41 
    42 class Thread1 extends Thread {
    43     private Example example;
    44 
    45     public Thread1(Example example) {
    46         this.example = example;
    47     }
    48 
    49     @Override
    50     public void run() {
    51         example.excute();
    52     }
    53 }
    54 
    55 class Thread2 extends Thread {
    56     private Example example;
    57 
    58     public Thread2(Example example) {
    59         this.example = example;
    60     }
    61 
    62     @Override
    63     public void run() {
    64         example.excute1();
    65     }
    66 }

    执行结果如下

    excute:0
    excute:1
    excute:2
    excute:3
    excute:4
    excute1:0
    excute1:1
    excute1:2
    excute1:3
    excute1:4

    例子程序4所达到的效果和例子程序2的效果一样,都是使得两个线程的执行顺序进行,而不是并发进行,当一个线程执行时,将object对象锁住,另一个线程就不能执行对应的块。

    synchronized方法实际上等同于用一个synchronized块包住方法中的所有语句,然后在synchronized块的括号中传入this关键字。当然,如果是静态方法,需要锁定的则是class对象。  

    可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。

    结论:

      synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;

      synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。

    原创文章来源:http://www.cnblogs.com/mengdd/archive/2013/02/16/2913806.html

  • 相关阅读:
    Facebook主页照片和封面照片的尺寸要求
    NopCommerce源码架构详解
    Razor语法大全
    IIS 8 上传图片 上传文件报413错误及仅Https下报413问题,IIS高版本的配置方案及Web.config配置全解
    (一) MongoDB安装与配置
    Net Core 导出PDF
    ASP.NET Core AutoWrapper 自定义响应输出
    浅谈Docker之Docker数据持久化Bind Mount和Volume(转)
    搭建mysql集群
    MySQL错误:Can't connect to MySQL server (10060) 解决方案
  • 原文地址:https://www.cnblogs.com/jing-er/p/5012517.html
Copyright © 2020-2023  润新知