线程同步 synchronized 和 Lock 的区别

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差不多,但能够显现精准唤醒 

你可能感兴趣的:(线程同步,同步,多线程)