zoukankan      html  css  js  c++  java
  • Java多线程学习篇(二)synchronized

    synchronized 有二种修饰方法:
    1. 修饰一个方法

      synchronized public void runTest{
          /**/
      }
    2. 修饰一个代码块

      public void runTest{
          synchronized( /*某一对象或某一类*/ ){
              /* 代码块 */
          }
      }
    synchronized 的作用范围分为修饰一个类和修饰一共对象
    当修饰一个对象时,不同线程的同一对象调用相同代码会发生堵塞
    当修饰一个时,不同线程的同一类调用相同代码会发生堵塞
    修饰静态方法相当于修饰类

    定义一个类(用于验证 synchronized 的作用范围)

    public class Test implements Runnable {
    
        public static int Count = 0;
    
        @Override
        public void run() {
            runTest();
        }
    
        public void runTest() {
            for (int i = 0; i < 5; ++i) {
                Count++;
                System.out.println(Thread.currentThread().getName() + " " + Count);
            }
        }
    }

    当 synchronized 修饰一个方法时

    1. 若方法为非静态方法,作用的范围是一个对象

      不同线程的同一对象调用该方法时会发生堵塞

      //调用相同的对象
      synchronized public void runTest() { // 修饰非静态的方法 for (int i = 0; i < 5; ++i) { Count++; System.out.println(Thread.currentThread().getName() + " " + Count); } }

      通过以下代码调用

      Test test = new Test();
      Thread thread_one = new Thread( test, "Thread_ONE" );
      Thread thread_two = new Thread( test, "Thread_Two" );
      thread_one.start();
      thread_two.start();

      结果是

      Thread_ONE 1
      Thread_ONE 2
      Thread_ONE 3
      Thread_ONE 4
      Thread_ONE 5
      Thread_Two 6
      Thread_Two 7
      Thread_Two 8
      Thread_Two 9
      Thread_Two 10

      由于该调用是二个thread任务中的对象是同一个test,第一个thread任务运行时,会将第二个任务堵塞

      若对象不是同一个,则不会发生堵塞

      //调用二个不同的对象
      Thread thread_one = new Thread(new Test(), "Thread_ONE");
      Thread thread_two = new Thread(new Test(), "Thread_Two");
      thread_one.start();
      thread_two.start();

      结果是

      Thread_Two 2
      Thread_ONE 1
      Thread_Two 3
      Thread_ONE 4
      Thread_Two 5
      Thread_ONE 6
      Thread_Two 7
      Thread_ONE 8
      Thread_Two 9
      Thread_ONE 10
    2.  当修饰的方法为静态的方法时,作用的范围是一个类,而非一个对象。

      不同线程的相同类调用该方法时都会发生堵塞

      //调用相同的对象
      Test test = new Test();
      Thread thread_one = new Thread( test, "Thread_ONE" );
      Thread thread_two = new Thread( test, "Thread_Two" );
      thread_one.start();
      thread_two.start();
      
      //调用二个不同的对象
      Thread thread_one = new Thread(new Test(), "Thread_ONE");
      Thread thread_two = new Thread(new Test(), "Thread_Two");
      thread_one.start();
      thread_two.start();

      结果都为

      Thread_ONE 1
      Thread_ONE 2
      Thread_ONE 3
      Thread_ONE 4
      Thread_ONE 5
      Thread_Two 6
      Thread_Two 7
      Thread_Two 8
      Thread_Two 9
      Thread_Two 10

    当 synchronized 修饰一个代码块时,作用的范围看括号内的内容。若括号内为对象,则范围是一个对象;若括号内为类,则范围是一个类

    1. 若括号内为对象,则范围是一个对象,效果和 synchronized 修饰非静态方法一样

      public void runTest() {
          synchronized (this) { // 括号内为一个对象
              for (int i = 0; i < 5; ++i) {
                  Count++;
                  System.out.println(Thread.currentThread().getName() + " " + Count);
              }
          }
      }

      效果

      //调用相同的对象
      Test test = new Test();
      Thread thread_one = new Thread( test, "Thread_ONE" );
      Thread thread_two = new Thread( test, "Thread_Two" );
      thread_one.start();
      thread_two.start();
      //发生类堵塞
      //Thread_ONE 1
      //Thread_ONE 2
      //Thread_ONE 3
      //Thread_ONE 4
      //Thread_ONE 5
      //Thread_Two 6
      //Thread_Two 7
      //Thread_Two 8
      //Thread_Two 9
      //Thread_Two 10
      
      
      //调用二个不同的对象
      Thread thread_one = new Thread(new Test(), "Thread_ONE");
      Thread thread_two = new Thread(new Test(), "Thread_Two");
      thread_one.start();
      thread_two.start();
      // 没有发生堵塞
      //Thread_Two 2
      //Thread_ONE 1
      //Thread_Two 3
      //Thread_ONE 4
      //Thread_Two 5
      //Thread_ONE 6
      //Thread_Two 7
      //Thread_ONE 8
      //Thread_Two 9
      //Thread_ONE 10
    2.  若括号内为类,则范围是一个对象,效果和 synchronized 修饰静态方法一样

      public [static] void runTest() {  
          synchronized (Test.class) { // 括号内为一个类
              for (int i = 0; i < 5; ++i) {
                  Count++;
                  System.out.println(Thread.currentThread().getName() + " " + Count);
              }
          }
      }

      二种调用都发生堵塞 

      Thread_ONE 1
      Thread_ONE 2
      Thread_ONE 3
      Thread_ONE 4
      Thread_ONE 5
      Thread_Two 6
      Thread_Two 7
      Thread_Two 8
      Thread_Two 9
      Thread_Two 10

    要注意的一点是,以上的例子不同的线程都是调用同一段的代码,有可能会出现误点,认为一段代码块对应一把锁。

    synchronized的锁锁住的是对象,或者某个类。一个对象或者一个类对应一把锁。

    class Example {
        public synchronized void printfA()
        {
            //synchronized (this) {
                for(int i = 1; i <= 100; ++i)
                    System.out.println("A "+i);
            //}
        }
        public synchronized void printfB()
        {
            //synchronized (this) {
                for(int i = 1; i <= 100; ++i)
                    System.out.println("B "+i);
            //}
        }
    }
    public class test{
        Example ex = new Example();
        RunA runa = new RunA();
        RunB runb = new RunB();
        class  RunA implements Runnable {
            @Override
            public void run() {
                ex.printfA();
            }
        }
        class  RunB implements Runnable {
            @Override
            public void run() {
                ex.printfB();
            }
        }
        public static void main(String[] args)
        {  
            test t = new test();
            Thread A = new Thread( t.runa );
            Thread B = new Thread( t.runb );
            A.start(); B.start();
        }
    }

    注意这个例子,当线程 A 调用了 printfA() 方法时,获得了对象 ex 的锁,而线程 B 调用了 printfB()方法,这里虽然和线程 A 调用的方法不一样,但是锁是同一个,都是 ex 的锁,所以锁已经被线程 A 获得,线程 B 将被堵塞,只是等线程 A 释放锁(运行完printfA())线程B才会运行。(有可能线程 B 比线程 A 先获得锁),所以 A 和 B 的输出不会出现交叉。

    小结:多线程中 锁 的最主要的目的就是为了确保多线程间的安全性,同一时间只用一个线程对相同一处内存进行操作,而程序运行的本质之一也就是对内存的操作。静态类在编译的时候就分配类内存,锁的对象要是是静态的,那操作都是同一个内存,作用对象也是这个类;锁的对象要是非静态的,也就是是一个对象,那在对象实例化的时候分配内存,不同的对象有着不同的内存。

    参考:http://tutorials.jenkov.com/java-concurrency/synchronized.html

  • 相关阅读:
    游标
    mobaxterm安装与使用(root_35/37/36/121)
    美团笔试--修改矩阵
    美团笔试1--螺旋矩阵
    assert函数
    2019年头条笔试--围坐分糖果
    邻值查找——stl函数lower_bound和upper_bound的使用
    动态规划练习1--腾讯暑期实习正式批
    Windows 7下硬盘安装CentOS6.4的解决方法
    Sublime Text 3 的一些基本使用方法和配置
  • 原文地址:https://www.cnblogs.com/lkcc/p/8178963.html
Copyright © 2011-2022 走看看