Jakartase_多线程 --- 线程同步方法(一)--- 同步块(肆)

一、引言、大纲

  • 在并发编程中发生的最常见的一种情况是超过一个执行线程使用共享资源。在并发
    应用程序中,多个线程读或写相同的数据或访问同一文件或数据库连接这是正常
    的。这些共享资源会引发错误或数据不一致的情况,我们必须通过一些机制来避免这些错误。
  • 临界区:是访问同一共享资源在同一时间不能被超过一个线程执行的代码块。
  • 同步机制:当一个线程想要访问一个临界区, 它使用其中的一个同步机制来找出是否有任何其他线程执行临界区

    • 如果没有,这个线程就进入临界区。
    • 否则,这个线程通过同步机制暂停直到另一个线程执行完临界区。
    • 当多个线程正在等待一个线程完成执行的一个临界 区,JVM选择其中一个线程执行,其余的线程会等待直到轮到它们。
  • Java 同步块(synchronized block)用来标记方法或者代码块是同步的
  • 大纲

       1、Java同步关键字(synchronzied)
       2、实例方法同步
       3、静态方法同步
       4、实例方法中同步块
       5、静态方法中同步块
       6、Java同步示例
    

二、具体介绍

2.1、Java 同步关键字(synchronized)

2.1.1、作用

  • Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。

2.1.2、原理

  • 在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。

2.1.3、同步块的分类

   实例方法
   静态方法
   实例方法中的同步块
   静态方法中的同步块
  • 上述同步块都同步在不同对象上。实际需要那种同步块视具体情况而定

①、实例方法同步

    写法一:修饰的是一个方法
    public synchronized void method1 (int a){
        //do something;
    }
    写法二:修饰的是一个代码块,都是锁定了整个方法时的内容
    public void method1 (int a){
        synchronized (this) { //do something; }
    }
  • 同步对象:实际上是对调用该方法的对象加锁,俗称”对象锁”。
  • 注意事项

     在定义接口方法时不能使用synchronized关键字。
     构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
    

②、静态方法同步

    public static synchronized void method2 (int a){
        //do something!
    }
  • 同步对象:作用的对象是这个类的所有对象(静态方法是属于类的而不属于对象的)
  • demo

        public class SyncThread implements Runnable{
            private static int count;
            public SyncThread() {
                count = 0;
            }    
            public synchronized static void method(){
                for (int i = 0; i < 3; i++){
                    try {
                        System.out.println(Thread.currentThread().getName() + ":" + (count++));
                        Thread.sleep(100);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }//for
            }//method
            public synchronized void run() {
                method();
            }
        }//SynThread
        
        //在main里:有两个不同的实例
        SyncThread syncThread1 = new SyncThread();
        SyncThread syncThread2 = new SyncThread();
        new Thread(syncThread1, "SyncThread1").start();
        new Thread(syncThread2, "SyncThread2").start();
        
          输出:
          SyncThread1:0 
          SyncThread1:1 
          SyncThread1:2 
          SyncThread1:3 
          SyncThread1:4 
          SyncThread2:5 
          SyncThread2:6  都分别执行了3次循环
    • 结果:syncThread1syncThread2SyncThread的两个实例,但在thread1thread2并发执行时却保持了线程同步
    • 分析:run()中调用了静态方法method(),而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁
    • 修饰类中的静态方法 == 修饰1个类synchronized(SyncThread.class){}

③、方法中的同步块

   public  class MyClass {
       public synchronized void log1(String msg1, String msg2){
            log.writeln(msg1);
            log.writeln(msg2);
       }
       public void log2(String msg1, String msg2){
            synchronized(this){
                log.writeln(msg1);
                log.writeln(msg2);
            }
       }
   }
  • 分析:每次只有一个线程能够在两个同步块中任意一个方法内执行,这跟中的写法二大同小异

④、静态方法中的同步块

   public  class MyClass {
       public static synchronized void log1(String msg1, String msg2){
            log.writeln(msg1);
            log.writeln(msg2);
       }
       public void log2(String msg1, String msg2){
            synchronized(MyClass.class){
                log.writeln(msg1);
                log.writeln(msg2);
            }
       }
   }
  • 分析:两个方法不允许同时被多个线程访问

三、总结

  • A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象
  • B. 如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  • C. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  • D. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
  • E. 死锁先留个悬念
    原文作者:Shonan
    原文地址: https://segmentfault.com/a/1190000019853852
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞