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乐观机制。

 三、线程池

posted @ 2020-05-30 21:26  naray  阅读(390)  评论(0编辑  收藏  举报