八锁问题

场景一

import java.util.concurrent.TimeUnit;

/*
* 场景一:sendMsg和call都是同步方法,先打印谁?
* 答案:A-->发短信
* 分析:被synchronized修饰属于同步方法,这里争夺地是同一把锁,谁先调用锁就先执行
* */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        //Phone phone2 = new Phone();
        new Thread(()->{phone1.sendMsg();},"A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{phone1.call();},"B").start();
    }
}
class Phone{
    public synchronized void sendMsg(){
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景二

import java.util.concurrent.TimeUnit;

/*
 * 场景二:sendMsg和call都是同步方法,不过被先调用的sendMsg方法块内增加了休眠,先打印谁?
 * 答案:A-->发短信
 * 分析:线程A和线程B争夺地是同一把锁,谁先调用就先执行
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        //Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class Phone {
    public synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景三

import java.util.concurrent.TimeUnit;

/*
 * 场景三:call方法改为普通方法,sendMsg改为同步方法,先打印谁?
 * 答案:B-->打电话
 * 分析:线程A在争夺抢锁,线程B和锁无关,虽然线程A被先调用执行,但sendMsg内的休眠使它比call晚了1s
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        //Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class Phone {
    public synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景四

import java.util.concurrent.TimeUnit;

/*
 * 场景四:新建另一个对象phone2,两个线程分别使用两个对象调用方法,先打印谁?
 * 答案:B-->打电话
 * 分析:synchronized锁的是调用的对象,这里有两把锁,线程B不必等待线程A释放锁即可执行call方法
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone {
    public synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景五

import java.util.concurrent.TimeUnit;

/*
 * 场景五:两个方法前面用static synchronized修饰,先打印谁?
 * 答案:A-->发短信
 * 分析:static synchronized锁的是调用的类,所以这里还是同一把锁,线程B必须等待线程A释放锁才能执行call方法
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        //Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class Phone {
    public static synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public static synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景六

import java.util.concurrent.TimeUnit;

/*
 * 场景六:一个方法前面用static synchronized修饰,另一个前面用synchronized修饰,先打印谁?
 * 答案:B-->打电话
 * 分析:sendMsg方法锁的是类,call方法锁的是对象,这里有两把锁
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        //Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class Phone {
    public static synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public static void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景七

import java.util.concurrent.TimeUnit;

/*
 * 场景七:两个方法前面都用static synchronized修饰,同时新建另外一个对象phone2,先打印谁?
 * 答案:A-->发短信
 * 分析:这里锁的是类,所以两个对象争夺地还是同一把锁
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone {
    public static synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public static synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

场景八

import java.util.concurrent.TimeUnit;

/*
 * 场景八:一个方法前面用static synchronized修饰,另一个前面用synchronized修饰,两个对象分别调用不同方法,先打印谁?
 * 答案:B-->打电话
 * 分析:用static synchronized修饰锁的是类,用用synchronized修饰锁的是对象,所以两个对象争夺地是不同的两把锁
 * */
public class Lock8 {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendMsg();
        }, "A").start();
        //保证线程A先到达Runnable状态,线程B后到达Runnable状态
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone {
    public static synchronized void sendMsg() {
        //当线程A、B抢夺的不是同一把锁时,sendMsg方法休眠使线程B的call方法先被打印出来
        //这里必须保证该休眠与Main方法内的休眠存在差值
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->发短信");
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "-->打电话");
    }
}

总结

  1. 对于八锁问题,首先明白Main方法内休眠和先调用方法内休眠的不同作用,这是前提条件;
  2. synchronized修饰锁的是调用的对象,被static synchronized修饰锁的是调用的类;
  3. 不管两个线程执行地是不是一个对象的方法,只要争夺地是同一把锁就按线程顺序执行。
原文地址:https://www.cnblogs.com/i-chase/p/14215296.html