多线程是实现并发机制的一种有效手段。在 Java 中实现多线程有两种手段,一种是继承 Thread 类,另一种就是实现 Runnable/Callable 接口。
java.util.concurrent 包是专为 Java并发编程而设计的包。类图如下:
一、同步
1.1 synchronized 关键字,用来给对象和方法或者代码块加锁。
同步方法 synchronized T methodName(){} 同步方法锁定的是当前对象。当多线程通过同一个对象引用多次调用当前同步方法时,需同步执行。静态同步方法,锁的是当前类型的类对象。
同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。
锁可重入。 同一个线程,多次调用同步代码,锁定同一个锁对象,可重入。子类同步方法覆盖父类同步方法。可以指定调用父类的同步方法,相当于锁的重入。
当同步方法中发生异常的时候,自动释放锁资源。不会影响其他线程的执行。
同步代码块 T methodName(){ synchronized(object){} } 同步代码块在执行时,是锁定 object 对象。当多个线程调用同一个方法时,锁定对象不变的情况下,需同步执行。 同步代码块的同步粒度更加细致,效率更高。 T methodName(){ synchronized(this){} } 当锁定对象为 this 时,相当于同步方法。
同步代码一旦加锁后,那么会有一个临时的锁引用执行锁对象,和真实的引用无直接关联。在锁未释放之前,修改锁对象引用,不会影响同步代码的执行。
Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步方法 并不是由 monitor enter 和 monitor exit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。在 Java 虚拟机(HotSpot)中,monitor 是由 ObjectMonitor 实现的。
1 /** 2 * synchronized关键字 3 * 锁对象。synchronized(this)和synchronized方法都是锁当前对象。 4 */ 5 package concurrent.t01; 6 7 import java.util.concurrent.TimeUnit; 8 9 public class Test_01 { 10 private int count = 0; 11 private Object o = new Object(); 12 13 public void testSync1(){ 14 synchronized(o){ 15 System.out.println(Thread.currentThread().getName() 16 + " count = " + count++); 17 } 18 } 19 20 public void testSync2(){ 21 synchronized(this){ 22 System.out.println(Thread.currentThread().getName() 23 + " count = " + count++); 24 } 25 } 26 27 public synchronized void testSync3(){ 28 System.out.println(Thread.currentThread().getName() 29 + " count = " + count++); 30 try { 31 TimeUnit.SECONDS.sleep(3); 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 } 36 37 public static void main(String[] args) { 38 final Test_01 t = new Test_01(); 39 new Thread(new Runnable() { 40 @Override 41 public void run() { 42 t.testSync3(); 43 } 44 }).start(); 45 new Thread(new Runnable() { 46 @Override 47 public void run() { 48 t.testSync3(); 49 } 50 }).start(); 51 } 52 53 }