二、线程关键字的使用(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();
    }
}
String加锁实例

输出结果:

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();
        }
    }

 

posted @ 2020-09-21 21:59  迷迭香111  阅读(189)  评论(0)    收藏  举报