juc

JUC编程

1、线程和进程

线程、进程

进程:一个可运行的程序。

一个进程可以包含多个线程,至少包含一个线程。

java默认的线程有2个:mian主线程、gc垃圾回收线程

线程:进程中的一个执行任务。

java不能开启线程,而是通过本地方法native,java没有权限开线程

并发、并行

并发编程:并发、并行

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

  • cpu一核(一瞬间只能处理一个东西),模拟出来多条线程,通过快速交替,来实现多条线程(其实这是一种假象)

并行:意思就是多个人一起行走

  • cpu多核:多个线程可以同时执行;可以使用线程池操作

查看cpu核数的代码:

system.out.println(Runtime/getRuntime().availableProcessors())

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

线程有几个状态

new 新生
Runnable 运行
BLOCKED 阻塞
WAITING 等待,死等
TUMED_WAITING 超时等待
TERMINATED 终止

wait/sleep 区别

1、来自不同的类

wait=>object

sleep=>Thread

2、关于锁的释放

wait会释放锁

sleep不会释放(抱着锁睡觉了,就不会释放了)

3、使用的范围是不同的

wait:必须在同步代码块中

sleep:可以在任何地方睡

4、是否需要捕获异常

wait不需要捕获异常

sleep必须捕获异常

2、lock锁(重点)

传统synchronized

package com.xiao.demo01;
//基本的卖票例子

/**
 *真正的多线程开发公司中的开发,降低偶合性
 * 线程就是一个单独的资源类,没有任何附属的操作
 * 1.属性,方法
 */
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        //并发,多个线程操作同一个资源类, 把资源类丢入线程
         Ticket ticket = new Ticket();
         //函数式接口
        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
//资源类 oop面向对象
class Ticket{
    //属性,方法
    private int number = 50;

    //卖票的方式
    public synchronized void sale(){
        if (number > 0){
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
        }

    }

}

lock接口

package com.xiao.demo01;

import jdk.nashorn.internal.ir.Block;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        //并发,多个线程操作同一个资源类, 把资源类丢入线程
        Ticket ticket = new Ticket();
        //函数式接口
        new Thread(()-> { for (int i = 0; i < 40; i++) ticket.sale();},"A").start();
        new Thread(()-> { for (int i = 0; i < 40; i++) ticket.sale();},"B").start();
        new Thread(()-> { for (int i = 0; i < 40; i++) ticket.sale();},"C").start();
    }


}
//lock三部曲
//1、new ReentrantLock();
//2、 lock.lock(); 加锁
//3、lock.unlock();  解锁
class Ticket2{
    //属性,方法
    private int number = 50;

    Lock lock = new ReentrantLock();

    //卖票的方式
    public  void sale(){

        lock.lock();

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

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

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

synchronized和lock区别

  1. s ynchronized 内置的java关键字,lock是一个java类
  2. synchronized 无法判断获取锁的状态,lock可以判断是否获得了锁
  3. synchronized 会自动释放锁,lock必须要手动解锁,如果不释放锁,就会死锁
  4. synchronzied线程1(获得锁,阻塞)、线程2(等待,傻傻的等);lock锁就不一定会等待下去;try尝试
  5. synchronized可重入锁,不可以终端的,非公平;lock,可重入锁,可以判断锁,非公平(可以自己设置)
  6. synchronized适合锁少量的代码同步问题;lock锁大量的同步代码。

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

3、生产者和消费者问题

生产者和消费者问题 synchronized版 wait notify

package com.xiao.demo01;

public class SaleTicketDemo03 {
    public static void main(String[] args) {
        A a = new A();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    a.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Q").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"W").start();

    }
}
class A{
    private int number = 0;

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

        this.notifyAll();
    }
}

问题存在:四个线程的情况下这么写还安全吗?

解决方法:将a类中的if判断改为while判断,防止虚假唤醒

package com.xiao.demo01;

public class SaleTicketDemo03 {
    public static void main(String[] args) {
        A a = new A();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Q").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"W").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"e").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"r").start();

    }
}


class A{
    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==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);

        this.notifyAll();
    }

}

juc版的生产者和消费者问题 lock await signal

package com.xiao.demo01;

import jdk.nashorn.internal.ir.Block;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo03 {
    public static void main(String[] args) {
        A a = new A();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Q").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"W").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"e").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    a.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"r").start();

    }
}


class A{
    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();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public  void decrement () throws InterruptedException {
        lock.lock();
        try {
            while (number==0){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

任何一个新的技术,绝对不是仅仅只有覆盖了原来的技术,优势和补充

Condition 精准的通知和唤醒线程

package com.xiao.demo01;

/**
 * a执行完调用b,b执行完调用c,c执行完调用a
 */

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class C {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        },"A").start();
        new Thread(()->{  for (int i = 0; i < 10; i++) {
                data.printB();
        }},"B").start();
        new Thread(()->{  for (int i = 0; i < 10; i++) {
                data.printC();
        }},"C").start();
    }
}

class Data {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1;//1a 2b 3c

    public void printA(){
        lock.lock();

        try {
            //业务,判断 执行 通知
            while (number!=1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"aaaaa");
            //唤醒,唤醒指定的人b
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            while (number!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"bbbbb");
        number=3;
        condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try {
            while (number!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"ccccc");
            number=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

4、8锁现象

如何判断锁的是谁,永远的知道什么是锁,锁到底锁的是谁。

package com.xiao.demo01;

import java.util.concurrent.TimeUnit;

/**
 * 8锁,就是关于锁的8个问题
 * 1、标准情况下,两个线程先打印发短信还是打电话  发短信。打电话
 */
public class Test {
    public static void main(String[] args) {
        Prone prone = new Prone();
        new Thread(()->{ prone.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{prone.call();},"B").start();

    }
}
class Prone{
    //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("打电话");
    }
}

package test;

import java.util.concurrent.TimeUnit;

/**
 * 3、增加了一个普通方法后,先执行发短信还是hello  普通方法
 * 4、两个对象,两个同步方法,发短信还是打电话?  打电话
 */
public class Test2 {
    public static void main(String[] args) {
        //两个对象
        Prone2 prone1 = new Prone2();
        Prone2 prone2 = new Prone2();
        new Thread(()->{prone1.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{prone2.call();},"B").start();

    }
}
class Prone2{
    //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 hello(){
        System.out.println("hello");
    }
}
package test;

import java.util.concurrent.TimeUnit;

/**
 * 7、一个静态的同步方法,一个普通的同步方法,一个对象   先哪个?
 * 8、一个静态的同步方法,一个普通的同步方法,两个个对象   先哪个?
 */
public class Test4 {
    public static void main(String[] args) {
        //两个对象
        Prone4 prone1 = new Prone4();
        Prone4 prone2 = new Prone4();

        new Thread(()->{prone1.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{prone2.call();},"B").start();

    }
}
//phone3唯一的一个class对象
class Prone4{
    //静态同步方法  锁的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("打电话");
    }
}

小结

new this具体的一个手机

static class唯一的一个模版

5、集合类不安全

list不安全

package com.xiao.demo01;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        //并发下 ArrayList 不安全的
        /**
         * 解决方案
         * 1、Vector解决
         * 2、List<string> list = Collections.synchronizedList(new ArrayList<>());
         * 3、List<string> list = new CopyOnWriteArrayList<>();
         */
        //CopyOnWrite 写入时复制  cow 计算机程序设计领域的一种优化策略
        //多个线程使用的时候,list,读取的时候,固定的,写入,覆盖
        //在写入的时候避免覆盖,造成数据问题
        //读写分离
        //CopyOnWrite比Vector好在前一个不用synchronized,效率相比就会更快
        List<string> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();

        }
    }
}

set不安全

package com.xiao.demo01;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;/** * 同理可证  java.util.ConcurrentModificationException * 1、Set<string> set = Collections.synchronizedSet(new HashSet<>()); * 2、Set<string> set = new CopyOnWriteArraySet<>(); */public class SetTest {    public static void main(String[] args) {      //  Set<string> set = new HashSet<>();       // Set<string> set = Collections.synchronizedSet(new HashSet<>());        Set<string> set = new CopyOnWriteArraySet<>();        for (int i = 1; i < 30; i++) {            new Thread(()->{                set.add(UUID.randomUUID().toString().substring(0,5));                System.out.println(set);            },String.valueOf(i)).start();        }    }}

hashSet底层是什么? (就是hashmap)

public HashSet() {    map = new HashMap<>();}//add set本质就是map key是无妨重复的public boolean add(E e) {  return map.put(e, PRESENT)==null;}

6、Callable(简单)

1、可以有返回值

2、可以抛出异常

3、方法不同run()/call()

代码测试

callable通过runable的实现类futuretask来沟通到thread

package com.xiao.demo01;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
     //   new Thread(new Runnable).start();
       // new Thread().start();  //怎么启动Callable
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);
        //适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();//结果会缓存,效率高

        Integer o = (Integer) futureTask.get();//获取callable的返回结果 
        //  这个get方法可能产生堵塞,把它放到最后
        //使用异步
        System.out.println(o);

    }
}
class MyThread implements Callable<integer>{

    @Override
    public Integer call() {
        System.out.println("call()");
        return 1024;
    }
}

7、常用的辅助类

7.1、CountDownLatch

package com.xiao.demo01;import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo {    public static void main(String[] args) throws InterruptedException {        //总数是6        CountDownLatch countDownLatch = new CountDownLatch(6);        for (int i = 1; i <= 6; i++) {            new Thread(()->{                System.out.println(Thread.currentThread().getName()+"go out");                countDownLatch.countDown();            },String.valueOf(i)).start();        }        countDownLatch.await();//等待计数器归零,然后再向下执行        System.out.println("Close Door");    }}

原理:

countDownLatch.countDown();数量-1

countDownLatch.await();//等待计数器归零,然后再向下执行

每次有线程调用countdown()数量-1,假设计数器为0,countDownLatch.await();就会被唤醒,继续执行

7.1、CyclicBarrier

package com.xiao.demo01;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo {    public static void main(String[] args) {        /**         * 集齐7颗龙珠召唤神龙         *         */        //召唤龙珠的线程        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{            System.out.println("召唤神龙成功");        });        for (int  i = 0;  i < 7;  i++) {            final int temp = i;            //Lambda 能操作到i吗?            new Thread(()->{                System.out.println(Thread.currentThread().getName()+"收集"+temp+"龙珠");                try {                    cyclicBarrier.await();                } catch (InterruptedException e) {                    e.printStackTrace();                } catch (BrokenBarrierException e) {                    e.printStackTrace();                }            }).start();        }    }}

7.3、Semaphore

package com.xiao.demo01;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);


        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                //acquire得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //release释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

原理:

acquire得到,假设如果已经满了,等待,等待被释放为止

release释放,会将当前的信号量释放-1,然后唤醒等待的线程

作用:多个共享资源互斥的使用,并发限流,控制最大的线程数

8、读写锁

ReadWriteLock

read读可以被多线程同时读

Write写的时候只能有一个线程去写

package com.xiao.demo01;


import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 独占锁(写锁)一次只能被一个线程占有
 * 共享锁(读锁)多个线程可以同时占有
 * ReadWriteLock
 * 读-读  可以共存
 * 读-写  不能共存
 * 写-写  不能共存
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();

        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }

        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }

    }
}

//加锁的
class MyCacheLock{
    private volatile Map<string,object> map = new HashMap<>();
    //读写锁,更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //存,写,写入的时候只希望同时只有一个线程写
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            readWriteLock.writeLock().unlock();
        }

    }
    //取,读,所有人都可读
    public void get(String key){
        readWriteLock.readLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }

    }
}
/**
 * 自定义缓存
 */

class MyCache{
    private volatile Map<string,object> map = new HashMap<>();
    //存,写
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入"+key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName()+"写入OK");
    }
    //取,读
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取OK");
    }
}

juc</string,object></string,object>

juc

上一篇:perl变量


下一篇:unittest框架基本使用