zoukankan      html  css  js  c++  java
  • Java 多线程及线程间通信(Synchronized和Volatile关键字)

    一、多线程创建

      1. 继承Thread类,重写run()方法

     1     public class MyThread extends Thread {
     2 
     3         @Override
     4         public void run() {
     5             // TODO:
     6         }
     7     }
     8 
     9     MyThread myThread = new MyThread();
    10     myThread.start();

      2. 实现Runnable接口

     1     public class MythreadBRunnable implements Runnable {
     2 
     3         @Override
     4         public void run() {
     5             
     6         }
     7     }
     8 
     9     MythreadBRunnable runnable = new MythreadBRunnable();
    10     Thread thread = new Thread(runnable);
    11     thread.start();

      3. start()方法和run()方法区别

        run()方法是线程具体执行的实现,run()执行完后,线程就结束了。

        start()方法是开启线程,但是,要注意的是start()方法开启线程,并不代表立即执行,这个要看系统何时调用执行。开启线程代表线程进入就绪状态。

      4. 两种启动线程区别

        (1)继承Thread类,由于Java中类继承是单继承,不同于C++,在Java中为弥补这一缺点,通过实现Runnable接口来弥补这一缺点。用接口方法比Thread继承更灵活。

        (2)当开启多个线程时,使用继承Thread类方式,就必须产生相应多个Thread线程,而使用Runnable接口就只实现一个Runnable,通过实例Runnable接口创建对象,再将实例传递给Thread,并且调用start()开启线程。这样,做比使用继承Thread类要更方便。

    二、多线程通信

     (一)synchronized关键字

      1. synchronized关键字

        (1)synchronized对象锁

    public class SynchronizedObject {
        
        synchronized public void methodA() {
            
        }
        
        public void methodB() {
            synchronized (this) {
                // TODO:  
            }
        }
    }

         synchronized关键字修饰methodA()方法,methodA()方法是一个实例方法,而调用methodA()方法,肯定要创建一个对象,通过对象调用方法(syncObj.methodA())。这个对象就是SynchronizedObject类的对象,所以,就是synchronized关键字就是将这个对象加锁了。

         所以,synchronized关键字修饰methodA()与methodB()中,synchronized(this){}为this代表的对象加锁,所以,这两种方式锁住的是同一个对象,两种方式的效果是一样的。这就是synchronized对象锁。 

      2. 实践

      使用synchronized关键字修饰类方法和成员方法:

     1 public class CustomSyncThread extends Object {
     2 
     3     private static final String TAG = "CustomLock";
     4 
     5     public CustomSyncThread() {
     6 
     7     }
     8 
     9     synchronized public static void methodA() {
    10         try {
    11             Log.d(TAG, "MethodA 进入");
    12             Thread.sleep(2000);
    13             Log.d(TAG, "MethodA 离开");
    14         } catch (InterruptedException e) {
    15             e.printStackTrace();
    16         }
    17     }
    18 
    19     synchronized public static void methodB() {
    20         try {
    21             Log.d(TAG, "MethodB 进入");
    22             Thread.sleep(2000);
    23             Log.d(TAG, "methodB 离开");
    24         } catch (InterruptedException e) {
    25             e.printStackTrace();
    26         }
    27     }
    28 
    29     synchronized public void methodC() {
    30         try {
    31             Log.d(TAG, "methodC 进入");
    32             Thread.sleep(2000);
    33             Log.d(TAG, "methodC 离开");
    34         } catch (InterruptedException e) {
    35             e.printStackTrace();
    36         }
    37     }
    38 
    39     synchronized public void methodD() {
    40         try {
    41             Log.d(TAG, "methodD 进入");
    42             Thread.sleep(2000);
    43             Log.d(TAG, "methodD 离开");
    44         } catch (InterruptedException e) {
    45             e.printStackTrace();
    46         }
    47     }
    48 }

      多线程调用

        1. 多线程调用类方法

     1         new Thread(new Runnable() {
     2             @Override
     3             public void run() {
     4                 CustomSyncThread.methodA();
     5             }
     6         }).start();
     7 
     8         new Thread(new Runnable() {
     9             @Override
    10             public void run() {
    11                 CustomSyncThread.methodB();
    12             }
    13         }).start();

        输出:

    1 04-25 14:24:28.210 3937-3978/com.naray.demo D/CustomLock: methodB 进入
    2 04-25 14:24:30.214 3937-3978/com.naray.demo D/CustomLock: methodB 离开
    3 04-25 14:24:30.215 3937-3979/com.naray.demo D/CustomLock: methodA 进入
    4 04-25 14:24:32.216 3937-3979/com.naray.demo D/CustomLock: methodA 离开

        从输出可以看出synchronized是为类方法添加的是类锁,所以,同一时刻只有一条线程可以方法该类。

      2. 多线程调用对象方法

     1         final CustomSyncThread syncThread = new CustomSyncThread();
     2         new Thread(new Runnable() {
     3             @Override
     4             public void run() {
     5                 syncThread.methodC();
     6             }
     7         }).start();
     8 
     9         new Thread(new Runnable() {
    10             @Override
    11             public void run() {
    12                 syncThread.methodD();
    13             }
    14         }).start();

        输出:

    1 04-25 14:35:31.898 4374-4416/com.naray.demo D/CustomLock: methodD 进入
    2 04-25 14:35:33.899 4374-4416/com.naray.demo D/CustomLock: methodD 离开
    3 04-25 14:35:33.900 4374-4415/com.naray.demo D/CustomLock: methodC 进入
    4 04-25 14:35:35.902 4374-4415/com.naray.demo D/CustomLock: methodC 离开

        从输出可以看出synchronized为对象添加的是对象锁。

      3. 多线程调用对象方法和类方法

     1         final CustomSyncThread syncThread = new CustomSyncThread();
     2         new Thread(new Runnable() {
     3             @Override
     4             public void run() {
     5                 CustomSyncThread.methodA();
     6             }
     7         }).start();
     8 
     9         new Thread(new Runnable() {
    10             @Override
    11             public void run() {
    12                 syncThread.methodC();
    13             }
    14         }).start();

        输出结果:

    1 04-25 14:38:16.261 4544-4589/com.naray.demo D/CustomLock: methodC 进入
    2 04-25 14:38:16.262 4544-4588/com.naray.demo D/CustomLock: MethodA 进入
    3 04-25 14:38:18.263 4544-4589/com.naray.demo D/CustomLock: methodC 离开
    4 04-25 14:38:18.263 4544-4588/com.naray.demo D/CustomLock: MethodA 离开

        从上面结果可以看出,类锁和对象锁是互不干扰的。

       4. 多线程调用对象方法,在对象方法中调用其它的对象方法,在同一个对象锁中会有什么效果?

        CustomSyncThread类:

     1 public class CustomSyncThread extends Object {
     2 
     3     private static final String TAG = "CustomLock";
     4 
     5     public CustomSyncThread() {
     6 
     7     }
     8 
     9     synchronized public void methodC() {
    10         try {
    11             Log.d(TAG, "methodC 进入");
    12             this.methodD();
    13             Thread.sleep(2000);
    14             Log.d(TAG, "methodC 离开");
    15         } catch (InterruptedException e) {
    16             e.printStackTrace();
    17         }
    18     }
    19 
    20     synchronized public void methodD() {
    21         try {
    22             Log.d(TAG, "methodD 进入");
    23             Thread.sleep(2000);
    24             Log.d(TAG, "methodD 离开");
    25         } catch (InterruptedException e) {
    26             e.printStackTrace();
    27         }
    28     }
    29 }

        输出结果:

    1 04-25 14:53:48.876 4833-4877/com.naray.demo D/CustomLock: methodC 进入
    2 04-25 14:53:48.876 4833-4877/com.naray.demo D/CustomLock: methodD 进入
    3 04-25 14:53:50.878 4833-4877/com.naray.demo D/CustomLock: methodD 离开
    4 04-25 14:53:52.880 4833-4877/com.naray.demo D/CustomLock: methodC 离开

        从上面的结果可以看出,同一个对象锁下对象的成员方法调用另一个成员方法,是允许调用的。

     (二)线程间通信

      每个线程都有自己的工作内存,在线程执行过程中用到某个变量时,会将主内存的变量copy到线程的工作内存,在线程完成读取、修改、赋值操作后将变量写回到主内存。因此多线程同时用到同一个变量时,无法保证变量值的一致性,这就涉及到多线程的特性:原子性、有序性、可见性。

    1. volatile 关键字:使用volatile关键的变量在多线程用到此变量时,是不允许将此变量从主内存中copy到线程的工作内存中,多线程修改或者使用的变量都是主内存中的变量。当其它线程修改此变量后其它线程会同时收到变量被修改的值。从变量内存地址上说就是多线程操作的变量的内存地址是同一个。
    2. synchronized关键字:synchronized有一个监听器Monitior,多线程操作同一个对象时,对象内存使用synchronized关键字修饰,那么就会和同一个Monitior监听器,在synchronized代码块中MonitiorEnter和MonitiorExit标识。一个线程操作中,没有退出前,其它线程是不能进入操作的。
    3. volatile和synchronized区别:volatile只能用于主内存中,并且volatile只能用于变量。而synchronized可以修饰方法与代码块能用于主内存和副内存中。

     (三)Java Lock类

      是通过Java实现的锁机制,synchronized是托管于JVM虚拟机执行。synchronized性能比lock低。synchronized是CPU悲观锁机制,lock是CPU乐观机制。

     三、线程池

  • 相关阅读:
    二分查找 【数组的二分查找】
    二分答案 [TJOI2007]路标设置
    队测 逆序对 permut
    【线段树】 求和
    状压DP Sgu223 骑士
    状压DP Poj3311 Hie with the Pie
    状压DP入门 传球游戏之最小总代价
    状压DP 小W选书籍
    状压DP [Usaco2008 Nov]mixup2 混乱的奶牛
    __gcd()用法
  • 原文地址:https://www.cnblogs.com/naray/p/8944336.html
Copyright © 2011-2022 走看看