JUC并发(一)

目录

线程和进程

并发、并行

线程状态

wait/sleep区别

LOCL锁(重点)

1 传统的synchronized锁

2 Synchronized和Lock锁的区别

 synchronized写法

 lock Condition写法

锁是什么,如何判断锁的是谁

小结


线程和进程

进程:

  • 一个进程往往可以包含多个线程,至少包含一个!
  • Java默认有几个线程? 2 个 :mian、GC。

线程:开了一个进程 Typora,写字,自动保存(线程负责的)

Java无法直接开线程,是调用native本地方法,底层的C++,JAVA无法直接操作硬件

并发、并行

并发编程:并发,并行
并发:多个线程操作同一个资源

  •     并发:cpu只有一个核:多线程操作同一个资源(cpu通过线程间的快速交替,模拟出来多条线程,看似并行,实际串行)
  •     并行:cpu有多个核,多个线程可以同时执行,可以通过线程池完成

并发编程的本质:充分利用CPU的资源

public class test01 {
    public static void main(String[] args) {
        //获取CPU核数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

线程状态

public enum State {
        //新生
        NEW,

        //运行
        RUNNABLE,

       //阻塞
        BLOCKED,

        //等待(死死的等)
        WAITING,

        //超时等待(到期就不等了)
        TIMED_WAITING,

       //终止
        TERMINATED;
    }

wait/sleep区别

    来自不同的类
            wait()来自Object类,sleep()来自Thread类
    关于锁的释放
            wait()会释放锁,sleep()不会释放锁,可以理解为抱着锁睡觉
    使用范围不同
            wait()只能在同步代码块中使用,sleep()可以在任何地方使用
    是否需要捕获异常
            wait()不需要捕获异常,sleep()必须要捕获异常

LOCL锁(重点)

公平锁:十分公平,先来后到,排队
非公平锁:不公平,可以插队
(默认是非公平锁,是为了公平,比如一个线程要3s,另一个线程要3h,难道一定要让3h的锁先来就先执行吗)


1 传统的synchronized锁

  • 在公司真正的多线程开发中,线程就是一个单独的资源类,没有任何附属的操作(类中只有属性和方法),为了降低耦合性,不会用类去实现接口,因为实现类接口就不是OOP编程了,而且实现了接口的话耦合性变高,如果让类实现了Runnable,这个类就只是一个线程类了

在这里插入图片描述
在这里插入图片描述

//lock三部曲
// 1. new ReentrantLock()
// 2. lock.lock(); //加锁
// 3. finally => lock.unlock(); //解锁
class Ticket2 {
    //属性,方法
    private int num = 30;

    Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock(); //加锁

        try {
            // 业务代码
            if (num > 0) {
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"张票,剩余:"+num);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); //解锁
        }
    }
}

2 Synchronized和Lock锁的区别

  •     Synchronized:内置的Java关键字,Lock:是一个java类
  •     Synchronized:无法判断获取锁的状态,Lock:可以判断是否获取了锁
  •     Synchronized:会自动释放锁,Lock:必须要手动释放锁,如果不释放锁,会死锁
  •     Synchronized:线程1(获得锁,阻塞),线程2(等待,傻傻的等),Lock:Lock锁不一定会等待,Lock有一个方法Lock.tryLock();会去尝试获取锁
  •     Synchronized:可重入锁,不可以在中断,非公平, Lock:可重入,可以判断锁,可以设置公平或者非公平
  •     Synchronized:适合锁少量的代码同步问题,Lock:适合锁大量的同步代码

 synchronized写法

public synchronized void increment() throws InterruptedException {
        while (num != 0) {
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"=>"+num);
        //通知其他线程,我+1完了
        this.notifyAll();
}

 lock Condition写法

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (num != 0) {
                //等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            //通知其他线程,我+1完了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

锁是什么,如何判断锁的是谁

 问题1:标准情况下,先打印发短信,还是先打印打电话?结果是:先打印发短信,后打印打电话
 原因:不能回答先调用A线程,这是错误的,不是先调用先执行,这是锁的问题,因为被Synchronized修饰的 方法,锁的对象是方法的调用者,所以调用两个方法的对象都是phone,但是现在phone只有一个,也就是说着两个方 法现在用的是同一把锁,谁先拿到,谁就先执行


public class Test1 {
    public static void main(String[] args) {
        phone phone = new phone();
        new Thread(()->{ phone.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone.call(); },"B").start();
    }
}

class phone{
    //synchronized 锁的对象是方法的调用者
    public synchronized void sendSms() {
        System.out.println("发短信");
    }
    public synchronized  void call() {
        System.out.println("打电话");
    }
}

问题2:给发短信方法加延时4s,程序的执行情况

结果:经过4s之后打印发短信,然后立马打印打电话。

public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

问题3:当调用普通方法,而不是synchronized方法时,程序的执行情况

结果:1秒先打印Hello,4秒后打印发短信
先执行普通方法,因为普通方法没有锁,不受锁的影响

public class Test1 {
    public static void main(String[] args) {
        phone phone = new phone();
        new Thread(()->{ phone.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone.sayHello(); },"B").start();
    }
}

class phone{
    //synchronized 锁的对象是方法的调用者
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized  void call() {
        System.out.println("打电话");
    }
    public  void sayHello()
    {
        System.out.println("hello");
    }
}

问题4:两个对象分别调用synchronized方法时,程序的执行情况

结果:先打电话,后发短信。
两个对象,两个调用者,两把锁,因为锁不一样,所以耗时短的先输出。

public static void main(String[] args) {
		//两个对象,两个调用者,两把锁
        phone phone1 = new phone();
        phone phone2 = new phone();

        new Thread(()->{ phone1.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone2.call(); },"B").start();
    }

问题5:增加两个静态的同步方法,只有一个对象,程序的执行情况

结果:先发短信,后打电话。

public class Test1 {
    public static void main(String[] args) {
        phone phone = new phone();

        new Thread(()->{ phone.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone.call(); },"B").start();
    }
}

class phone{
    //static 静态方法,类一加载就有了,锁的是Class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized  void call() {
        System.out.println("打电话");
    }
}

问题6:两个静态的同步方法,两个对象,程序的执行情况

结果:先发短信,后打电话。
两个对象的Class模板只有一个,static锁的是Class

public class Test1 {
    public static void main(String[] args) {
        phone phone1 = new phone();
        phone phone2 = new phone();

        new Thread(()->{ phone1.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone2.call(); },"B").start();
    }
}

问题7:一个静态同步方法,一个普通同步方法,只有一个对象,程序的执行情况

结果:先打电话,后发短信。
不是同一个锁,锁调用者的不需要等待,锁Class的要等待

public class Test1 {
    public static void main(String[] args) {
        phone phone = new phone();

        new Thread(()->{ phone.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone.call(); },"B").start();
    }
}

class phone{
    //静态同步方法,锁的是Class模板
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //普通同步方法,锁的是调用者,不需要去等待锁
    public synchronized void call() {
        System.out.println("打电话");
    }
}

问题8:一个静态同步方法,一个普通同步方法,两个对象,程序的执行情况

结果:先打电话,后发短信。
两个锁,还是锁的对象不一样

public static void main(String[] args) {
        phone phone1 = new phone();
        phone phone2 = new phone();

        new Thread(()->{ phone1.sendSms(); },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{ phone2.call(); },"B").start();
    }

小结

普通同步方法:(new this) 锁的是调用者,是一个具体的对象
静态同步方法:(static Class) 锁的是Class模板,是唯一的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值