多线程

Posted lvsheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。


多线程

了解线程和进程的区别

进程:一个程序,QQ.exe  Music.exe  程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
Java默认是有2 个线程的   mian、GC
线程:开了一个进程 Typora,写字,自动保存(线程负责的) 对于Java而言:Thread、Runnable、Callable

并发、并行

并发编程:并发、并行

并发(多线程操作同一个资源)
CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替

并行(多个人一起行走)

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

多个线程操作同个资源

Synchronized(Java内置关键字)和Lock(接口)

1、Synchronized   内置的Java关键字,  Lock 是一个Java类

2、Synchronized  无法判断获取锁的状态,Lock  可以判断是否获取到了锁
3、Synchronized  会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁

4、Synchronized   线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下 去;
5、Synchronized    可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以 自己设置);
6、Synchronized     适合锁少量的代码同步问题,Lock  适合锁大量的同步代码

这里主要讲Lock接口

  实现类ReentrantLock无参构造默认是非公平锁 传入true既是公平锁,

 

 public static void main(String[] args) {
       // System.out.println(Runtime.getRuntime().availableProcessors());
       Tic tic=new Tic();
       new Thread(()->{ for (int i = 0; i <50 ; i++) tic.sale();},"A").start();
       new Thread(()->{ for (int i = 0; i <50 ; i++) tic.sale();},"B").start();
       new Thread(()->{ for (int i = 0; i <50 ; i++) tic.sale();},"C").start();
  }
}
class Tic1{
   int num=50;
   public void sale(){
       java.util.concurrent.locks.Lock lock=new ReentrantLock();
       lock.lock(); //加锁
       try {
           if(num>0){
               System.out.println(Thread.currentThread().getName()+"卖出"+(num--)+"票 剩余"+num);
          }
      }catch (Exception e){
      }finally {
           lock.unlock();  //解锁
      }
?
  }

Condition 精准的通知和唤醒线程

public class PcTo {
   public static void main(String[] args) {
       // System.out.println(Runtime.getRuntime().availableProcessors());
       Tic3 tic=new Tic3();
           new Thread(()->{ for (int i = 0; i <10 ; i++) {
               try {
                   tic.add();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
          },"A").start();
           new Thread(()->{ for (int i = 0; i <10 ; i++) {
               try {
                   tic.remove();
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
          },"B").start();
?
       new Thread(()->{ for (int i = 0; i <10 ; i++) {
           try {
               tic.remove2();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
      },"C").start();
?
?
  }
}
?
class Tic3{
   Lock lock=new ReentrantLock();
   Condition condition1=lock.newCondition();
   Condition condition2=lock.newCondition();
   Condition condition3=lock.newCondition();
   int num=0;
   public  void add() throws InterruptedException {
       lock.lock(); //上锁
       try {
           while(num!=0){  //当num不等于0 通知其他线程
               condition1.await();  //等待
          }
           System.out.println(Thread.currentThread().getName()+":aaaa"+num);
           num=1;
           condition2.signal();
      }catch (Exception e){
?
      }finally {
           lock.unlock();
      }
?
  }
?
   public  void remove() throws InterruptedException {
       lock.lock(); //上锁
       try {
           while(num!=1){  //当num不等于0 通知其他线程
               condition2.await();  //等待
          }
           System.out.println(Thread.currentThread().getName()+":bbbbbbbbb"+num);
           num=2;
           condition3.signal();
      }catch (Exception e){
?
      }finally {
           lock.unlock();
      }
?
  }
   public  void remove2() throws InterruptedException {
       lock.lock(); //上锁
       try {
           while(num!=2){  //当num不等于0 通知其他线程
               condition3.await();  //等待
          }
           System.out.println(Thread.currentThread().getName()+":ccccccc"+num);
           num=0;
           condition1.signal();
      }catch (Exception e){
?
      }finally {
           lock.unlock();
      }
?
  }
}

 

技术图片

集合类的不安全

  • List

    List list=new ArrayList();
      //源码
      public boolean add(E e) {
           ensureCapacityInternal(size + 1);  // Increments modCount!!
           elementData[size++] = e;
           return true;
      }

     普通的ArrayList 底层是没有Synchronized的

解决方法:
  1. List<String> list=new Vector<>();
      //Vector实现类 源码 是使用了synchronized
     public synchronized boolean add(E e) {
           modCount++;
           ensureCapacityHelper(elementCount + 1);
           elementData[elementCount++] = e;
           return true;
      }
  2. //通过List的父类 Collection
    List<String> list=Collections.synchronizedList(new ArrayList<>());
    ?
      public static <T> List<T> synchronizedList(List<T> list) {
           return (list instanceof RandomAccess ?
                   new SynchronizedRandomAccessList<>(list) :
                   new SynchronizedList<>(list));
      }
  3. /**为什么要用CopyOnWriteArrayList不用的他的父类
    个人认为 父类使用synchronized 而CopyOnWriteArrayList使用通过ReentrantLock
    第一 当synchronized第一个线程阻塞了第二线程就只能等待 而ReentrantLock就不一定了
    第二 Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
     CopyOnWrite 写入时复制   COW 计算机程序设计领域的一种优化策略;    
      多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)    
      在写入的时候避免覆盖,造成数据问题!    
      读写分离
    */
    List<String> list=new CopyOnWriteArrayList<>();
    ?
    //底层代码
       public boolean add(E e) {
           final ReentrantLock lock = this.lock;
           lock.lock();
           try {
               Object[] elements = getArray();
               int len = elements.length;
               Object[] newElements = Arrays.copyOf(elements, len + 1);
               newElements[len] = e;
               setArray(newElements);
               return true;
          } finally {
               lock.unlock();
          }
      }
    ?
  • Set

  public class SetTest {    
     public static void main(String[] args) {
         /**       Set<String> set = new HashSet<>(); 不安全 底层就是new了HashMap
             Set<String> set = Collections.synchronizedSet(new HashSet<>());
         ConcurrentHashMap是Java中的一个线程安全且高效的HashMap实现。
         平时涉及高并发如果要用map结构
         */
         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();  
  }
}
     
   

常用的辅助类

  • CountDownLatch

    //减法计数器
    public class CountDownTest {
       public static void main(String[] args) throws InterruptedException {
           CountDownLatch countDownLatch=new CountDownLatch(6);
           for (int i = 0; i <6 ; i++) {
               new Thread(()->{
                   System.out.println(Thread.currentThread().getName()+" GO out");
                   countDownLatch.countDown();  //减1
              }).start();
          }
           countDownLatch.await(); // 等待计数器归零,然后再向下执行
           System.out.println("close ");
      }
    }
  • CyclicBarrier

      

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
//加法计算器
public class CycliBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
            System.out.println("欧力士"); //当数量达到7 就会执行这里的代码
        });

        for (int i = 0; i <7 ; i++) {
           final int num=i;
             new Thread(()->{
                 System.out.println(num);
                 try {
                     cyclicBarrier.await();  // 等待
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 } catch (BrokenBarrierException e) {
                     e.printStackTrace();
                 }
             },String.valueOf(num)).start();

        }
        System.out.println();
    }
}
  • Semaphore
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 = 0; i < 6 ; i++) {
             int ito=i;
             new Thread(()->{
                 try {
                     semaphore.acquire(); // 获得,假设如果已经满了,等待,等待被释放为止
                     System.out.println(Thread.currentThread().getName()+"得到了车位");
                     TimeUnit.SECONDS.sleep(3);
                     System.out.println(Thread.currentThread().getName()+"离开了车位");
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }finally {
                     semaphore.release(); //释放,会将当前的信号量释放 + 1,然后唤醒等待的线程!
                 }
             },String.valueOf(ito)).start();
        }
    }
}

线程池

线程池:三大方法、7大参数、4种拒绝策略

  • 池化技术

程序的运行,本质:占用系统的资源! 优化资源的使用!=>池化技术

线程池、连接池、内存池、对象池///.....  创建、销毁。十分浪费资源

池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我

  • 线程池的好处:

1、降低资源的消耗

2、提高响应的速度

3、方便管理。
线程复用、可以控制最大并发数、管理线程

  • 三大方法

  • *  ExecutorService executorService = Executors.newCachedThreadPool();//可收缩 遇强则强
    *
    *  ExecutorService executorService1 = Executors.newSingleThreadExecutor();//单个线程
    *
    * ExecutorService executorService2 = Executors.newFixedThreadPool(5);//创建指定大小的线程
  • 4种拒绝策略

/*  new ThreadPoolExecutor.DiscardOldestPolicy() 队列满了 尝试和最早的竞争 也不会抛出异常
*
* new ThreadPoolExecutor.DiscardPolicy() 会丢掉任务 不会抛出异常
*
* new ThreadPoolExecutor.CallerRunsPolicy()  拿来的去哪里 现在是mian线程 所以让mian线程处理 也不会抛出异常
* new ThreadPoolExecutor.AbortPolicy()当队列已满的线程 加上最多线程的 还来 就会抛出
*/
  • 七大参数

    /** public ThreadPoolExecutor(int corePoolSize, //核心线程大小
    * int maximumPoolSize,  //最大核心线程大小
    *long keepAliveTime, //超时了 没有人用回自己释放
    * TimeUnit unit,    //超时单位
    * BlockingQueue<Runnable> workQueue, //注射队列
    *ThreadFactory threadFactory,  //线程工厂 创建线程的 一般不用
    *RejectedExecutionHandler handler   //拒绝策略
    *) {
    *     }
    */
public class Demo1 {
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors()); //获取cpu的he
        //自定义线程
            ExecutorService executorService3=new ThreadPoolExecutor(
                       2,  //核心 线程
                    5,   //最多
                    3,   //超时时间
                    TimeUnit.SECONDS, //单位
                    new LinkedBlockingDeque<>(3),  //相当于 银行的 候客区 当 核心线程 和候客区 满了 就会启用 最大线程
                    Executors.defaultThreadFactory(),
                    //拒绝策略
                    new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了 尝试和最早的竞争 也不会抛出异常 );
        for (int i = 0; i < 10; i++) {
            executorService3.execute(()->{
                System.out.println(Thread.currentThread().getName()+" OK");
            });
        }

    }
}

  说明

      上面代码的线程池 核心2个线程,最多5个线程,超时3秒,队列最多3个,拒绝策略和最早的线程竞争

       举列子:

        相当于 现在是一个银行 目前有人工窗口5个(最多线程), 但是目前只开启了2个窗口(核心线程),等待区有3个位置(队列),如果等待区没人,但是有两个人进来办理业务,目前是开启了2个窗口的,可以应付的来,不会开启另外三个窗口,但是只要这两个人在办理业务的过程中,又有人进来办理业务了,发现等待区还有三个位置(队列),就会等待,但是只要等待区满了(队列),而且2个窗口还在办理业务,这是时候就会开启另外的窗口(最多线程),在5个人在同时在办理业务的时候,还进来人,只能进去等待区去了,但是当5个窗口在办理业务,同时等待区页已经满了,在进人,这个时候,人工窗口和等待区已经满了,这个时候就需要采用4种拒绝策略了

拒绝策略看自己需求 就可以了

以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

多个请求是多线程吗

python小白学习记录 多线程爬取ts片段

多线程编程

多线程编程

python多线程