JUC总结

JUC总结

线程与进程

进程:是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。

线程:是进程的一个实体,是 cpu 调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。

java如何开启线程?

通过底层调用C语言,所以java本身无法创建线程!

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
    /* do nothing. If start0 threw a Throwable then
    it will be passed up the call stack */
            }
        }
    }

    // 本地方法,底层的C++ ,Java 无法直接操作硬件 
    private native void start0();

并发与并行

并发(多线程操作同一个资源)

  • CPU一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替

并行(多个人一起行走)

  • CPU多核 ,多个线程可以同时执行; 线程池

并行是在不同实体上的多个事件,并发是在同一实体上的多个事件

线程的几种状态

public enum State {
    // 新生
    NEW,
    // 运行 
    RUNNABLE,
    // 阻塞 
    BLOCKED,
    // 等待,死死地等 
    WAITING,
    // 超时等待 
    TIMED_WAITING,
    // 终止
    TERMINATED;
}

wait/sleep 区别

  1. sleep() 方法正在执行的线程主动让出 cpu(然后 cpu 就可以去执行其他任务),在 sleep 指定时间后 cpu 再回到该线程继续往下执行(注意:sleep 方法只让出了 cpu,而并不会释放同步资源锁);

    而 wait() 方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了 notify() 方法,之前调用 wait() 的线程才会解除 wait 状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify 的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说 notify 只是让之前调用 wait 的线程有权利重新参与线程的调度);

  2. sleep() 方法可以在任何地方使用,而 wait() 方法则只能在同步方法或同步块中使用;

  3. sleep() 是线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait() 是 Object 的方法,调用会放弃对象锁,进入等待队列,待调用 notify()/notifyAll() 唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态。

    wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!

Lock锁

传统Synchronized

  1. 修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁;

  2. 修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 。也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员(static 表明这是该类的一个静态资源,不管 new了多少个对象,只有一份,所以对该类的所有对象都加了锁)。

    所以如果一个线程 A 调用一个实例对象的非静态 synchronized 方法,而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁;

  3. 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。和 synchronized 方法一样,synchronized(this) 代码块也是锁定当前对象的。synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class 类上锁。这里再提一下:synchronized 关键字加到非 static 静态方法上是给对象实例上锁。另外需要注意的是:尽量不要使用 synchronized(String a) 因为 JVM 中,字符串常量池具有缓冲功能。

Lock接口

实现类:

  • ReentrantLock(可重入锁 常用)
  • ReentrantReadWriteLock.ReadLock(读锁)
  • ReentrantReadWriteLock.WriteLock(写锁)
public ReentrantLock() {
        sync = new NonfairSync(); //非公平锁
}

    
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();//公平锁
}

公平锁:十分公平:可以先来后到

非公平锁:十分不公平:可以插队 (默认)

Synchronized 和 Lock 区别

  1. Synchronized是内置的Java关键字, Lock 是一个接口

  2. Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁

  3. Synchronized会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁

  4. Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;

  5. Synchronized 可重入锁,不可以中断的,非公平;Lock 可重入锁,可以判断锁,非公平(可以自己设置);

生产者和消费者问题

Synchronized实现

class Data1 {
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
        while (number != 0) {
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + ":" + number);
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number != 1) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + ":" + number);
        this.notifyAll();
    }
}

lock实现

Sychronized---->lock(Lock)

wait---->await(Condition)

notifiy---->signal(Condition)

class Data2 {
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            condition.signalAll();
        } finally {
            lock.unlock();
        }

    }

    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number != 1) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + ":" + number);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

注意(虚假唤醒)

当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功。比如说买货,如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了 ,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁。

如何解决?必须将if改成while

Condition实现精准通知和唤醒线程

目的:使唤醒顺序为ABC有序

class Data3{
    int number =1; //1A 2B 3C
    Lock lock=new ReentrantLock();
    Condition condition1=lock.newCondition();
    Condition condition2=lock.newCondition();
    Condition condition3=lock.newCondition();
    public void printA(){
        lock.lock();
        try {
            while (number !=1){//2,3
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
            number=2;
            condition2.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printB(){
        lock.lock();
        try {
            while (number !=2){//1,3
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBBBBBB");
            number=3;
            condition3.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printC(){
        lock.lock();
        try {
            while (number !=3){ //1,2
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>CCCCCCC");
            number=1;
            condition1.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

测试

 public static void main(String[] args) {
        Data3 data3 = new Data3();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data3.printA();
            }
        },"A").start();
        new Thread(()->{for (int i = 0; i < 10; i++) {
            data3.printB();
        }},"B").start();
        new Thread(()->{for (int i = 0; i < 10; i++) {
            data3.printC();
        }},"C").start();
    }
    
结果:
A=>AAAAAAA
B=>BBBBBBB
C=>CCCCCCC
A=>AAAAAAA
B=>BBBBBBB
C=>CCCCCCC
...

你可能感兴趣的:(JUC总结)