synchronized 和 Lock 的区别:
① synchronized是内置的Java关键字,Lock是一个接口
② synchronized无法判断是否获取到锁,Lock可以判断是否获取到锁
③ synchronized会自动释放锁,Lock必须要手动释放锁,否则可能会死锁!
④ synchronized当有两个线程,其中一个得到锁,另一个线程则死等,Lock不一定会死等
⑤ synchronized是可重入锁 不可中断的 非公平锁,Lock是可重入锁 可以判断锁 可设为公平锁
⑥ synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码

卖票举例:

① 使用synchronized进行线程同步:

public class Demo01 { 
    public static void main(String[] args) { 
        // 并发,多线程操作统一资源
        Ticket ticket = new Ticket();

        new Thread(()->{  // Lambda表达式
            for (int i = 0; i < 60; i++) { 
                ticket.saleTicket();
            }
        },"张三").start();
        new Thread(()->{ 
            for (int i = 0; i < 60; i++) { 
                ticket.saleTicket();
            }
        },"李四").start();
        new Thread(()->{ 
            for (int i = 0; i < 60; i++) { 
                ticket.saleTicket();
            }
        },"王五").start();
    }
}
class Ticket{ 
    private int number = 50;

    public synchronized void saleTicket(){  // 同步方法
        if (number>0){ 
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票");
        }
    }
}

结果:

张三卖出了第50张票
张三卖出了第49张票
张三卖出了第48张票
张三卖出了第47张票
......(此处省略)
张三卖出了第5张票
张三卖出了第4张票
张三卖出了第3张票
张三卖出了第2张票
张三卖出了第1张票

② 使用Lock进行线程同步:

public class Demo02 { 
    public static void main(String[] args) { 
        // 并发,多线程操作统一资源
        Ticket2 ticket = new Ticket2();

        // Lambda表达式
        new Thread(()->{  for (int i = 0; i < 60; i++) ticket.saleTicket(); },"张三").start();
        new Thread(()->{  for (int i = 0; i < 60; i++) ticket.saleTicket(); },"李四").start();
        new Thread(()->{  for (int i = 0; i < 60; i++) ticket.saleTicket(); },"王五").start();
    }
}
class Ticket2{ 
    private int number = 50;
    Lock lock = new ReentrantLock();

    public void saleTicket(){ 
        lock.lock(); // 加锁
        try{ 
            if (number>0){ 
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票");
            }
        }finally { 
            lock.unlock(); // 解锁
        }
    }
}

结果:

张三卖出了第50张票
张三卖出了第49张票
张三卖出了第48张票
张三卖出了第47张票
张三卖出了第46张票
......(此处省略)
张三卖出了第5张票
张三卖出了第4张票
张三卖出了第3张票
张三卖出了第2张票
张三卖出了第1张票

生产者消费者问题

线程之间交替操作num,当num==0时,num加一,当num>0时,num减一
① 使用synchronized进行线程同步:

public class Demo03 { 
    public static void main(String[] args) { 
        Num num = new Num();
        new Thread(()->{ 
            for (int i = 0; i < 10; i++) { 
                try { 
                    num.increment();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{ 
            for (int i = 0; i < 10; i++) { 
                try { 
                    num.decrement();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Num{ 
    private int num = 0;
    // +1:当num==0时,加一
    public synchronized void increment() throws InterruptedException { 
        if (num!=0){ 
            this.wait(); // 等待
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"-->"+num);
        this.notifyAll(); // 唤醒,通知其他线程+1完毕
    }
    // -1:当num>0时,减一
    public synchronized void decrement() throws InterruptedException { 
        if (num==0){ 
            this.wait(); // 等待
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"-->"+num);
        this.notifyAll(); // 唤醒,通知其他线程-1完毕
    }
}

结果:

A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0

当有线程超过两个时,可能会出现虚假唤醒的情况,所以判断语句用while循环,避免虚假唤醒

public class Demo04 { 
    public static void main(String[] args) { 
        Num2 num = new Num2();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.increment();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.decrement();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.increment();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.decrement();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Num2{ 
    private int num = 0;
    // +1:当num==0时,加一
    public synchronized void increment() throws InterruptedException { 
        while (num!=0){   // 使用while循环,避免虚假唤醒
            this.wait(); // 等待
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"-->"+num);
        this.notifyAll(); // 唤醒,通知其他线程+1完毕
    }
    // -1:当num>0时,减一
    public synchronized void decrement() throws InterruptedException { 
        while (num==0){   // 使用while循环,避免虚假唤醒
            this.wait(); // 等待
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"-->"+num);
        this.notifyAll(); // 唤醒,通知其他线程-1完毕
    }
}

结果:

A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0

② 使用Lock进行线程同步:
Condition:同步监视器

public class Demo05 { 
    public static void main(String[] args) { 
        Num3 num = new Num3();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.increment();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.decrement();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.increment();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                try { 
                    num.decrement();
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Num3{ 
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    // +1:当num==0时,加一
    public void increment() throws InterruptedException { 
        lock.lock();
        try { 
            while (num!=0){ 
                condition.await(); // 等待 相当于synchronized中的wait()
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"-->"+num);
            condition.signalAll(); // 唤醒,通知其他线程+1完毕 相当于synchronized中的notifyAll()
        }finally { 
            lock.unlock();
        }
    }
    // -1:当num>0时,减一
    public void decrement() throws InterruptedException { 
        lock.lock();
        try { 
            while (num==0){ 
                condition.await(); // 等待 相当于synchronized中的wait()
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"-->"+num);
            condition.signalAll(); // 唤醒,通知其他线程-1完毕 相当于synchronized中的notifyAll()
        }finally { 
            lock.unlock();
        }
    }
}

结果:

A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
C-->1
B-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
A-->1
D-->0

Condition 实现精准通知唤醒:

// A执行完执行B,B执行完执行C,C执行完执行A
public class Demo06 { 
    public static void main(String[] args) { 
        Num4 num = new Num4();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                num.printA();
            }
        },"A").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                num.printB();
            }
        },"B").start();
        new Thread(()->{ 
            for (int i = 0; i < 5; i++) { 
                num.printC();
            }
        },"C").start();
    }
}
class Num4{ 
    private int num = 1;
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    public void printA(){ 
        lock.lock();
        try { 
            while (num!=1){ 
                condition1.await(); //等待
            }
            num = 2;
            System.out.println(Thread.currentThread().getName()+"-->"+"AAA");
            condition2.signal(); // 唤醒B
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }finally { 
            lock.unlock();
        }
    }
    public void printB(){ 
        lock.lock();
        try { 
            while (num!=2){ 
                condition2.await(); //等待
            }
            num = 3;
            System.out.println(Thread.currentThread().getName()+"-->"+"BBB");
            condition3.signal(); // 唤醒C
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }finally { 
            lock.unlock();
        }
    }
    public void printC(){ 
        lock.lock();
        try { 
            while (num!=3){ 
                condition3.await(); //等待
            }
            num = 1;
            System.out.println(Thread.currentThread().getName()+"-->"+"CCC");
            condition1.signal(); // 唤醒A
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }finally { 
            lock.unlock();
        }
    }
}

结果:

A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
A-->AAA
B-->BBB
C-->CCC
Lock用法跟synchronized差不多,但能够显现精准唤醒 

本文地址:https://blog.csdn.net/weixin_43912697/article/details/114272399