飞道的博客

JUC并发编程

306人阅读  评论(0)

多线程实现方式1

public class ThreadDemo {
    public static void main(String[] args) {
    //获取当前线程名城
        System.out.println(Thread.currentThread().getName());//main
        MyThread myThread = new MyThread();
        //run()仅仅封装线程执行的代码
        //start()首先启动线程 ,在由JVM调用run方法

        //myThread.start();
        //myThread.start();
        //java.lang.IllegalThreadStateException
        //start是启动线程 启动俩次会报错
        //所以创建俩个线程
        MyThread myThread1 = new MyThread();
        myThread.setName("张三");
        myThread1.setName("李四");
        myThread.start();
        myThread1.start();

    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println(getName()+" "+i);
        }
    }
}

线程调度

分时调度

抢占式调度(默认)

线程优先级

setPriority () 给线程设置优先级 默认值为5 值得范围1-10

1)、线程休眠 Thread.sleep(int)

2)、线程礼让 Thread.yield() 让其他线程走 在一定程度让线程执行更和谐

3)、线程守护(后台线程) public final void setDemo()

  1. 、线程加入 public final void join() 等待线程终止

线程状态

多线程实现方式2

实现Runnable

重写run方法

创建MyRunnable对象

创建Thread对象 把MyRunnable对象作为参数传递

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread,"张三");
        Thread thread2 = new Thread(myThread,"李四");
        thread1.start();
        thread2.start();


    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

有了第一种为什么还要第二种呢? 第一种是继承 如果一个类已经有父类他就不能继承Thread 有局限性

模拟卖票

使用继承Thread的方式 我们发现每个人都卖了100张 因为创建了三个MyThread对象 那就加static关键字

但是我们不想加关键字怎么办?使用实现Runnable接口的方式 ,这样就只会创建一个对象,多个线程共享这一个变量 运行发现 还是不对,某张票可能卖了多次 而且还出现了负数票 ,这是因为A线程卖第100张票的时候还没有减票数 B也进来卖第100张票,负数票同理 A卖第1张的时候还没减票数的时候 B也进入方法 C也进入方法

A 卖第1张票 ticket =0 B卖0张票 ticket =-1 C卖-1 ticket=-2

这就叫线程安全

Synchronized

解决线程安全 :既然多线程有多条语句操作共享数据 那我们就把多条语句包起来 使多条语句变成原子性的

synchronized (o):对象锁解释:一个对象对应一把锁 A线程进入会把该方法后会把锁的钥匙带走 B来了之后 没有钥匙 就会等待 如果每个线程进来都new一个对象锁 new的时候会出现钥匙 所以达不到锁的目的

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread,"张三");
        Thread thread2 = new Thread(myThread,"李四");
        Thread thread3 = new Thread(myThread,"王五");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class MyThread implements Runnable{
    private int ticket=100;
    private Object o = new Object();
    @Override
    public void run() {
      while (true) {
          synchronized (o) { //对象是关键  
              
              if(ticket>0) {
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
                  ticket--;
              }
              else break;;
          }
        }
    }
}

如果方法一进去就是run 那就可以把synchaonized加在方法上 对象默认是this

以前学的线程安全类

静态方法的锁是当前类class对象

创建线程安全的集合 Connections.synchronizedList(new HashMap()) 返回一个线程安全的hashmap集合

Lock

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

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread,"张三");
        Thread thread2 = new Thread(myThread,"李四");
        Thread thread3 = new Thread(myThread,"王五");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class MyThread implements Runnable{
    private int ticket=1000;
    private Object o = new Object();
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
           lock.lock();
               try {
                 if(ticket>0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
                    ticket--;
                }
                    else break;;
               }
               finally {
                   lock.unlock();
               }

        }
    }
}

线程死锁

public class ThreadDemo {
    public static void main(String[] args) {
        DieLock dieLock = new DieLock(true);
        DieLock dieLock1 = new DieLock(false);
        dieLock.start();
        dieLock1.start();
    }
}

class MyLock{
    public static Object oA = new Object();
    public static Object ob = new Object();
}

class DieLock extends Thread{
    private boolean flag;
    public DieLock(boolean flag){
        this.flag=flag;
    }
    @Override
    public void run() {
        if(flag){
            synchronized (MyLock.oA){
                System.out.println("ifoA");
                synchronized (MyLock.ob){
                    System.out.println("ifob");
                }
            }
        }
        else {
            synchronized (MyLock.ob){
                System.out.println("elseob");
                synchronized (MyLock.oA){
                    System.out.println("elseoA");
                }
            }
        }
    }
}

线程死锁有什么解决办法呢? 线程通信

线程通信

生产者与消费者

有下面几个问题

1 生产者生产了一半 消费者抢到资源 (消费者和生产者加锁 必须是同一个锁对象)

2 消费者先抢到资源 还没有生产任何东西

3 生产者一直生产 消费者抢不到怎么办

4生产者生产了一个 消费者得到N个怎么办

等待唤醒机制

Object类提供了俩个方法 wait()等待 和notify()唤醒

/*
    资源类 Studnet
    生产者 GetStudnet
    消费者 SetStudnet
    测试类 ThreadDemo
 */
public class ThreadDemo{
    public static void main(String[] args) {
        Student s = new Student();
        SetStudent setStudent = new SetStudent(s);
        GetStudent getStudent = new GetStudent(s);
        Thread t1 = new Thread(setStudent,"生产者");
        Thread t2 = new Thread(getStudent,"消费者");
        t2.start();
        t1.start();


    }

}
class Student{
    String name;
    int age;
    boolean flag;//如果是true就代表有数据
}

class SetStudent implements Runnable{
    Student s;
    int x =0;
    public SetStudent(Student s){
        this.s=s;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (s){
               if(s.flag){
                   try {
                       s.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
                if(x%2==0) {
                   s.name = "张三";
                   s.age = 18;
               }
               else {
                   s.name="王五";
                   s.age=28;
               }
               x++;
               s.flag=true;
               s.notify();
           }
       }
    }
}

class GetStudent implements Runnable{
    Student s;
    public GetStudent(Student s){
       this.s=s;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (s){
                if(!s.flag){
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(s.age + "---" + s.name);
                s.flag=false;
                s.notify();
           }
       }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4X2aRZ1-1586532994642)(C:\Users\李嘉伟\AppData\Roaming\Typora\typora-user-images\image-20200407222926519.png)]

线程组

/*
    资源类 Studnet
    生产者 GetStudnet
    消费者 SetStudnet
    测试类 ThreadDemo
 */
public class ThreadDemo{
    public static void main(String[] args) {
       MyThread myThread = new MyThread();
       ThreadGroup threadGroup = new ThreadGroup("新的线程组");
        Thread t1 = new Thread(threadGroup,myThread,"生产者");
        Thread t2 = new Thread(threadGroup,myThread,"消费者");
        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());
        t2.start();
        t1.start();


    }

}
class MyThread implements Runnable{
    @Override
    public void run() {

    }
}

组的方便之处

通过组设置优先级和后台线程 等等 一次行操作多个线程 就是线程组的功能

多线程实现方式3

实现Callable接口

这个方式的特点就是有返回值

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

public class Demo{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //Future实现了Runable接口
        FutureTask futureTask = new FutureTask(new MyThread());
        Thread t = new Thread(futureTask);
        t.start();
        Object o = futureTask.get();
        System.out.println(o);
    }


}
class MyThread implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        //进入方法
        System.out.println("income");
        //返回值
        return 1024;
    }
}

注意:不管有多少new Thread(futureTask); 都只会启动一个线程

Synchronized和lock锁的区别

1、Synchronized是关键字 lock是接口

2、Synchronized 无法判断锁的状态 LOCK可以获得锁

3、Synchronized 会自动释放锁 LOCK必须手动释放锁 否则死锁

4、

求和案例

import java.util.concurrent.*;

public class ThreadDemo{
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建线程池 并指定线程数量
        ExecutorService service = Executors.newFixedThreadPool(2);
        //往线程池里放线程
        Future<Integer> f1 = service.submit(new MyThread(100));
        Future<Integer> f2 = service.submit(new MyThread(200));
        System.out.println(f1.get());
        System.out.println(f2.get());
        service.shutdown();
    }

}
class MyThread implements Callable<Integer> {
    Integer number;
    Integer sum=0;
    public MyThread(Integer number){
        this.number=number;
    }
    @Override
    public Integer call() throws Exception {
        for (int i = 0; i <= number; i++) {
            sum += i;
        }
        return sum;
    }
}

消费者生产者口诀

所有业务写在lock内

加锁 干活 修改资源 释放锁

1、 JUC

1.1 进程/并发

1.2 并发/并行

2、三个包

  • java.util.concurrent
  • java.util.concurrent.atomic
  • java.util.concurrent.locks

可重入锁:重复利用的锁

wait和sleep的区别

wait必须在同步代码块中使用 waite后会释放锁

sleep可以在任意地方使用 sleep后不会释放锁

匿名内部类实现多线程

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

public class ThreadDemo{
    public static void main(String[] args){
        Resources ticket = new Resources();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (ticket.ticket>0) {
                    ticket.slae();
                }
            }
        },"张三").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (ticket.ticket>0) {
                    ticket.slae();
                }
            }
        },"李四").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (ticket.ticket>0) {
                    ticket.slae();
                }
            }
        },"王五").start();
    }
}

//资源类
class Resources{
    int ticket=300;
    Lock lock = new ReentrantLock();
    public void slae(){
        lock.lock();
        try{
            if (ticket>0){
                System.out.println(Thread.currentThread().getName()+ticket--);
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
}

lambda表达式

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

public class ThreadDemo{
    public static void main(String[] args){
        Resources ticket = new Resources();
        new Thread(()->{ while (ticket.ticket>0) { ticket.slae(); }},"张三").start();
        new Thread(()->{ while (ticket.ticket>0) { ticket.slae(); }},"李四").start();
        new Thread(()->{ while (ticket.ticket>0) { ticket.slae(); }},"王五").start();
    }
}

//资源类
class Resources{
    int ticket=300;
    Lock lock = new ReentrantLock();
    public void slae(){
        lock.lock();
        try{
            if (ticket>0){
                System.out.println(Thread.currentThread().getName()+ticket--);
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
}

3、集合类线程不安全

1)、List

new Arraylist: new了一个容量是10的数组

扩容扩到多少 ?扩原来的一半+1 15 Arrays.copyof() 搬家 在扩22 取整

hasmap 初始容量16 阈值0.75 扩容一倍

arrayList线程不安全

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;


public class ThreadDemo{
    public static void main(String[] args) {
        List<String> list = new ArrayList();
        for (int i = 0; i <30 ; i++) {
            new Thread(()->
            { list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(Thread.currentThread().getName()+"-"+list);
            },String.valueOf(i)).start();

        }

    }
}

1)、故障现象

​ java.util.ConcurrentModificationException

2)、导致原因

在很多容器(比如ArrayList)中,都有一个变量记录你从结构上修改此容器的次数,叫做modCount,比如你调用add方法向容器里面增加了一个元素,或者你删除了其中的某个元素,这个值都会增加1.在对集合进行迭代的时候,这个值不能被改变,否则抛出
ConcurrentModificationException。简单地说就是你在遍历的时候,你自己不会去改变这个值,但是你在遍历的过程中发现这个值被改变了,那只有一种解释,其他人修改了集合导致这个值改变了。

3)、解决办法

1 : List list = new Vector<>();

2:List list = new collections.synchronized(new ArrayList)

3:List list = new CopyOnWriteArrayList();

4)、优化建议

CopyOnWriteArrayList

写的时候复制一份之前的ArrayList 加锁 其他线程读之前的ArrayList 写完之后 删除之前的ArrayList

2、Set

Set线程不安全 HashSet底层数据结构HashMap 使用CopyOnWriteArraySet() 创造线程安全的set

set底层是hashmap set丢进去的是 key value是源码定义的一个Object对象

3.map

map 线程不安全 线程安全 :new ConcurrentHashMap() map没有写时复制

map底层1.7使用数组加链表 当发生hash碰撞之后 会形成链表 1.8之后链表长度>8的时候会变成红黑树 数组扩容有个阈值 0.75 数组长度达到0.75会发生扩容 扩之前的一半

八个线程问题

public class ThreadDemo{
    public static void main(String[] args) throws InterruptedException {
        Resources res = new Resources();
        Resources res1 = new Resources();
        new Thread(new Runnable() {
            @Override
            public void run() {
                res.senmessage();
            }
        },"A").start();
        Thread.sleep(100);//停一下 保证线程A先执行
        Resources res1 = new Resources();
        new Thread(new Runnable() {
            @Override
            public void run() {
                res.senemail();
            }
        },"B").start();
    }
}

/*资源类
 */
class Resources {
    public synchronized void senmessage(){
        System.out.println("发短信");
    }
    public synchronized void senemail(){
        System.out.println("发邮件");
    }
}

1标准访问先发短信还是邮件 先发短信 ? :发短信 A线程进入资源类之后 锁住了资源类的所有synchronized方法

2发邮件中暂停4秒 先发邮件还是短信 ? :发短信 A线程进入资源类之后 锁住了资源类的所有synchronized方法

3新增普通sayhello方法 不加synchronized 先发短信还是sayhello?: sayhello 因为没上锁

4 新增手机 先打印什么:? 发邮件 因为不是同一个锁 A线程用 res锁 B线程用res1锁

5加static 同一个手机 先打印什么? 先打印短信 因为同一个类锁

6加static 俩个手机 先打印什么 ? 先打印短信 因为同一个类锁

7 一个静态同步 一个普通同步方法 先打印什么? 先打印 邮件 因为一个是类锁 一个是 this锁

8一个静态同步 一个普通同步方法 俩个手机 先打印什么? 发邮件 因为一个类锁 一个 this锁

还是卖票问题

public class Demo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i <10 ; i++) {
                        ticket.increment();
                    }
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i <10; i++) {
                        ticket.decrement();
                    }
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }

}
class Ticket{
    int number=0;
    public synchronized void increment() throws InterruptedException {
        if(number!=0){
            this.wait();
        }
        number++;
        System.out.println("生产了1张票");
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
            if (number==0){
                this.wait();
            }
            number--;
        System.out.println("取走一张票");
        this.notifyAll();
    }

}

1)、虚假唤醒

在上面的卖票代码中 如果在加入一个生产者线程和一个消费者线程 就会出现生产多张票和取走多张票的错误

因为?线程被唤醒的时候 他已经进入了判断语句 不管票数是多少他就会往下执行,API规定判断应该使用while不能使用if否则出现虚假唤醒,whileyou俩个作用 一个循环 一个被唤醒后在判断

为什么俩个线程不会出问题?

2)、Lock锁卖票

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

public class Demo {
    public static void main(String[] args) {
       Resource resource = new Resource();
        new Thread(()->{
            for (int i = 0; i <500 ; i++) {
                resource.sale();
            }
        },"张三").start();
        new Thread(()->{
            for (int i = 0; i <500 ; i++) {
                resource.sale();
            }
        },"李四").start();
        new Thread(()->{
            for (int i = 0; i <500 ; i++) {
                resource.sale();
            }
        },"王五").start();
    }
}
class Resource {
    private int ticket=300;
    Lock lock = new ReentrantLock();
    public void sale() {
        lock.lock();
        try{
            if(ticket>0) {
                System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
                ticket--;
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
}

使用Lock锁之后不能再使用synchronized 配套的wait和notify ,notifyAll

Lock锁 应该使用 lock.newCondition 获得 Condition 对象 使用该对象的await 和singal singalAll 替代

3)、精确唤醒线程

在synchronized中notify无法指定唤醒哪一个线程 下面使用Lock唤醒

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
A线程  打印5次  B线程打印10次   C线程打印15次   依次执行
 */
public class Demo {
    public static void main(String[] args) {
        Resource r = new Resource();
        new Thread(() -> {
            for (int i = 0; i <10 ; i++)
                r.print5();
        },"A").start();

        new Thread(() -> {
            for (int i = 0; i <10 ; i++)
             r.print10();
        },"B").start();

        new Thread(() -> {
            for (int i = 0; i <10 ; i++)
                r.print15();
        },"C").start();
    }
}

class Resource {
    private int num = 1; //标志位 1A打印 2B打印 3C打印
    private Lock lock = new ReentrantLock();
    private  Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    public void print5() {
        lock.lock();
        try {
            while (num!=1){
                c1.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
            num=2;
            c2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print10() {
        lock.lock();
        try {
            while (num!=2){
                c2.await();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
            num=3;
            c3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print15() {
        lock.lock();
        try {
            while (num!=3){
                c3.await();
            }
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
            num=1;
            c1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

countdownlatch

这个类可以指定哪一个线程 在什么时候执行

N可线程执行完毕 这个线程才可以执行 这些都可以设置

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

public class Demo{
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);//6为有6个线程结束之后才怎么怎么样
        for (int i = 0; i <6 ; i++) {
            new Thread(new Runnable(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"走出教室");
                    countDownLatch.countDown();//执行完减一个
                }
            },String.valueOf(i)).start();

        }
        //只有减为0的时候 才会执行否则等待唤醒
        countDownLatch.await();
        System.out.println("Main结束");
    }
}

CyclicBarrier

和countdownlatch相反 这个类是

semaphore

适用于多个线程抢多个资源

抢到资源 资源数减一 放弃资源 资源数+1

import java.util.concurrent.*;

public class Demo{
    public static void main(String[] args)   {
        Semaphore semaphore = new Semaphore(3);//指定3个资源
        for (int i = 0; i <6 ; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"\t抢到了车位");
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName()+"\t离开了车位");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();//离开车位 资源车位数+1
                }

            },String.valueOf(i)).start();

        }
    }
}

读写锁ReadWriteLock

为什么又出来了一个锁?Lock锁 把读和写操作都锁了 但是读的时候没必要锁

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

public class Demo{
    public static void main(String[] args)   {
        Book book = new Book();
        for (int i = 0; i <5 ; i++) {
            String a = String.valueOf(i);
              new Thread(()->{
                  book.put(a,a);
              },String.valueOf(i)).start();
        }
        for (int i = 0; i <5 ; i++) {
            String b = String.valueOf(i);
            new Thread(()->{
                book.get(b);
            },String.valueOf(i)).start();
        }
    }
}
class Book{
    private  Map<String,String> map = new HashMap<>();

    public void put(String key,String value){

        System.out.println(Thread.currentThread().getName()+"\t正在写入");
        map.put(key, value);
        System.out.println(Thread.currentThread().getName()+"\t写入完成");
    }
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"\t正在读"+key);
        String value =  map.get(key);
        System.out.println(Thread.currentThread().getName()+"\t读完成"+value);
    }
}

读写锁改进

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

public class Demo{
    public static void main(String[] args)   {
        Book book = new Book();
        for (int i = 0; i <5 ; i++) {
            String a = String.valueOf(i);
              new Thread(()->{
                  book.put(a,a);
              },String.valueOf(i)).start();
        }
        for (int i = 0; i <5 ; i++) {
            String b = String.valueOf(i);
            new Thread(()->{
                book.get(b);
            },String.valueOf(i)).start();
        }
    }
}
class Book{
    private  Map<String,String> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public void put(String key,String value){
        readWriteLock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t正在写入");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"\t写入完成");

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            readWriteLock.writeLock().unlock();
        }
    }
    public void get(String key){
        readWriteLock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t正在读"+key);
            String value =  map.get(key);
            System.out.println(Thread.currentThread().getName()+"\t读完成"+value);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            readWriteLock.readLock().unlock();
        }
    }
}

阻塞队列

接口BlockingQueue

当队列是空的 从队列获取元素的操作会被阻塞 直到队列有元素

当队列式慢的 向队列添加元素的操作会被阻塞 直到队列有位置

为什么需要BlockingQueue

好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都办了


BlockingQueue的实现类

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Demo {
    public static void main(String[] args) {
        //指定队列长度为3
        BlockingQueue blockingDeque = new ArrayBlockingQueue(3);
//        System.out.println(blockingDeque.add("a"));
//        System.out.println(blockingDeque.add("C"));
//        System.out.println(blockingDeque.add("D"));
//        System.out.println(blockingDeque.add("E"));
//        Exception in thread "main" java.lang.IllegalStateException: Queue full

        

    }
}

element方法返回队列头元素 没有就抛出NoSuchElementException异常

peek方法 返回对列头元素 没有就返回null+

线程池

方式获取线程池的3种方式

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo {
    public static void main(String[] args) {
        //可以指定线程池容量   一池3线程
        //ExecutorService service = Executors.newFixedThreadPool(3);
        //线程池只有一个线程 不能改变   一池1线程
        //ExecutorService service = Executors.newSingleThreadExecutor();
        //可变的线程数  一池N线程
        ExecutorService service = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i <10 ; i++) {
                service.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "aaa");
                });
            }

        }finally {
            service.shutdown();
        }

    }
}

线程池7大参数

坑爹题

面试问你获取线程池三种方式那种用的最多 ?

都不用 应该自定义 为什么?

源码中 将最大线程数设置为了21亿

阿里巴巴手册规定创建线程的标准方式 可以自己定义7个参数

Executor executor = new ThreadPoolExecutor(,,,,,,);

自定义参数

import java.util.concurrent.*;

public class Demo {
    public static void main(String[] args) {
        Executor poolExecutor = new ThreadPoolExecutor(
                2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),//这个地方不写默认也是21亿
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());//默认拒绝策略
        try {
            for (int i = 0; i <9 ; i++) {
                poolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "aaa");
                });
            }
        }finally {
            ((ThreadPoolExecutor) poolExecutor).shutdown();
        }
    }
}

最大接受的线程数=max+queue 超出后报RejectedExecutionException异常

拒绝策略


参数细化

最大线程数是随便的么 ?不是

分为CPU密集型和IO密集型

如果是CPU密集型

cpu核数+1

在java代码中可以获得 CPU核数

System.out.println(Runtime.getRuntime().availableProcessors());//得到内核数 

IO密集型

假设有10个线程经常要做IO操作 那线程数一般设置为20 也就是2倍大


转载:https://blog.csdn.net/weixin_44734751/article/details/105444882
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场