二、线程关键字的使用(synchronized、volatile)
1、synchronized方法与锁对象
- synchronized 方法锁的是对象,而不是方法
- synchronized 方法的非synchronized 类型方法,B线程可以异步调用,若方法也为synchronized 类型,B线程调用则需等待 A线程释放Object对象的Lock锁
- synchronized 方法锁的是对象,造成对象的不同实例的 synchronized 方法互不影响
- 举个例子:A对象new了不同实例,A1、A2、A3,这时候有线程B、C、D调用A1、A2、A3的 synchronized 方法,互不影响,无需等待其他线程释放锁
- synchronized 锁拥有可重入的功能,即当一个线程得到一个对象锁后,再次请求此对象锁是可以再次得到该对象的锁
- synchronized 锁出现异常时,锁自动释放,并且synchronized 方法不具有继承性(同步不具有继承性),并且持有的对象监视器只有一个,可以监视多个线程的访问,synchronized(this) 是锁定当前对象
- synchronized 关键字加到static静态方法上是给Class类加锁,而synchronized 关键字加到非static静态方法上是给对象上锁
举个例子:

public class Chap1Service { public synchronized void service1(){ System.out.println("Hello Service1"); service2(); } public synchronized void service2(){ System.out.println("Hello Service2"); } } public class Chap1Class { public static void main(String[] args) { Chap1Service chap1Service = new Chap1Service(); chap1Service.service1(); } } 输出结果: Hello Service1 Hello Service2
service1() 方法可以调用service2() 方法,证明锁是可重入的,如果不可重入的话,调用service1()方法获取对象锁还没释放,再去调用service()获取对象锁,这时候获取不到就会造成死锁
2、synchronized 同步语句块
- String类型的常量池特性,即A、B线程如果都使用相同的字符串(“LOCK”)作为加锁,由于String常量池的特性,所以只new一个对象实例,所以A、B线程持有的是同一个对象锁
- synchronized 同步语句块解决线程长时间等待另外线程释放对象锁的问题
- synchronized 同步代码块使得当前代码块的变量变为多个线程可见,即其他线程取得变量的值是从公共堆栈中取得
2.1 举个例子:

public class Service { public void print(String param){ try { synchronized (param){ while (true){ System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } }catch (InterruptedException e){ e.printStackTrace(); } } } public class ThreadA extends Thread{ private Service service; public ThreadA(Service service){ this.service = service; } @Override public void run() { super.run(); service.print("LOCK"); } } public class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { super.run(); service.print("LOCK"); } } public class Chap2Class { public static void main(String[] args) { Service service = new Service(); ThreadA threadA = new ThreadA(service); threadA.setName("threadA"); threadA.start(); ThreadB threadB = new ThreadB(service); threadB.setName("threadB"); threadB.start(); } }
输出结果:
threadA
threadA
threadA
由于A、B线程持有的是同一个String对象锁,所以A线程先持有对象锁,代码功能是死循环,所以一直都是A线程输出
2.3 举个例子:
public class Service { private boolean isContinueRun = true; private String anyThing = new String(); public void runMethod(){ while (isContinueRun){ synchronized (anyThing){ //如果删除这个代码块,则程序会死循环 } } System.out.println("停下来了! "); } public void stopMethod(){ isContinueRun = false; } } public class ServiceA extends Thread{ private Service service; public ServiceA(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.runMethod(); } } public class ServiceB extends Thread{ private Service service; public ServiceB(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.stopMethod(); } } public static void test5(){ try { Service service = new Service(); ServiceA serviceA = new ServiceA(service); serviceA.start(); Thread.sleep(1000); ServiceB serviceB = new ServiceB(service); serviceB.start(); System.out.println("已经发起停止的命令了! "); }catch (InterruptedException e){ e.printStackTrace(); } }
3、volatile关键字
- 使变量在多个线程中可见,即强制从公共堆栈中获取变量的值,而不是从线程私有数据栈中取得变量的值,并不处理变量的原子性
举个例子:以下代码功能不加volatile是死循环,加完volatile后可以进行停止
public class RunThread extends Thread{ // volatile不加的时候会死循环,加完之后避免死循环 private /*volatile*/ boolean running = true; public boolean isRunning() { return running; } public void setRunning(boolean running) { this.running = running; } @Override public void run() { System.out.println("进入 run 了"); while (running){ } super.run(); System.out.println("线程被停止了!"); } } public static void test4(){ try { RunThread runThread = new RunThread(); runThread.start(); Thread.sleep(1000); runThread.setRunning(false); System.out.println("已经赋值为false了"); }catch (InterruptedException e){ e.printStackTrace(); } }