多线程入门学习笔记


多线程学习笔记:

    synchronized关键字:既能够保证原子性也能够保证可见性。 被synchronized关键字修饰的对象会被加上一个互斥锁。
    
    synchronized是一个非公平锁,如果多个线程处于等待状态,那么这些线程是随机获取锁的。
    
    什么是互斥锁:同一时间只能被一个线程持有(注意:只有加了synchronized关键字同时会使用到这个对象的线程会呈现等待状态)
    
    synchronized锁定一个静态对象时是直接锁定该对象的CLASS对象
    
    synchronized所获得的锁是可以重入的:同一个class对象里面多个方法可以重复获得一个锁。
    
    子类的同步方法可以调用父类的同步方法,允许这样。
    
    线程出现异常的时候会直接释放锁,这时候在多线程情况下可能会读出错误数据
    
    volatile关键字:线程之间可见性,禁止指jmm对计算机指令的重排序
    
    一般来说线程读取某个对象都是将其放在自己的缓冲区,待对该对象操作完毕之后会将缓冲区更新后的数据写入堆里。
    
    如果是加了volatile关键字,当某个线程对该对象做了修改之后会提示其他线程,从而更新其他线程缓冲区引用对象的值。
    
    volatile关键字的效率比synchronized要高,volatile关键字是无锁同步的方式,但是不能够保证原子性(数据一致性)
    
    atoMic类里面能够保证原子性,但是只能单一使用atoMic里面的方法是才具有原子性。多个方法使用时可能会出现问题。
    
    synchronized: 细粒度的锁效率比粗粒度的锁效率高
    
    new object() 是新建一个对象,所以不会存在锁被占用的问题。最好不要使用string 常量做synchronized的对象
    
    wait() 将加锁的对象的当前使用线程置为等待状态,同时释放锁,此时其他线程可以调用此对象。唤醒线程的方法是notify和notifyall
    
    注意: notify 和 notifyAll 不会释放锁 
    
    注意:notify 是唤醒沉睡的线程和对象无关。 wait是使当前线程沉睡,!!!!。虽然都是对象的方法,但一定注意的是它操作的是线程!!!
    
    countdownlunch:门闩。不需要加锁也可实现沉睡和唤醒机制。
    
        latch.await()  等待
        latch.countdownl() 唤醒
    
    reentratlock:手动锁可重入锁,使用完毕后必须手动释放。
    
            // 获取reentratLock 实例
            Lock lock = new reentratlock();
            // 尝试获取锁
            lock.tryLock(5,TimeUnit.SECCOND); 尝试获取锁(指定时间,指定时间单位)
            // 设置线程可以被打断 , 设置了这个属性,主线程可以对该线程进行interrupt()打断
            lock.lockInterruptibly();
            // reentratLock可以设置公平锁,这样能够保证等待时间最久的线程获取线程
            
            例子:
            public class ReentratDemo extends Thread{
            
            // 设置公平锁
            private Lock lock = new ReentrantLock(true);

            public void run(){
                for (int i = 0; i < 100;i++){
                    try {
                        // 加上可重入锁
                        lock.lock();
                        System.out.println(Thread.currentThread().getName() + "获得锁");
                    }catch (Exception e){
                        System.out.println(e);
                    }finally {
                        // 释放锁:注意 reentratLock必须设置手动释放锁
                        lock.unlock();
                    }
                }
            }

            public static void main(String[] args) {
                ReentratDemo r = new ReentratDemo();
                Thread t1 = new Thread(r);
                Thread t2 = new Thread(r);

                t1.start();
                t2.start();
                }
            }
          
         
         object.wait()方法通常是个while一起使用的;
            
            while(size == max){                                     if(size == max){
              try{                                                    try{
                 this.wait()                                              this.wait()
              }catch(InterruntException e){                             }catch(InterruntException e){
                 sout(e);                                                  sout(e);
              }                                                          }
            }                                                           }
            forEach()                                              forEach()
            
        这里如果线程是被重新唤醒,还是会继续执行while中的判断        这里线程如果被唤醒,会直接执行wait()后面的代码,而不会再次去判断size == max,容易出现错误   
    
    
     
    synchronized 锁对象,可重入的互斥锁。抛出异常的时候会自动释放锁。
    lock:reentratLock: 手动释放的可重入互斥锁。tryLck可以尝试获取锁,lockInterruntaby 设置可以被打断 new 的时候可以指定reentratLock为公平锁
    
    wait()  被锁住的对象释放锁,并且使当前线程处于等待状态
    
    notify/notifyAll  唤醒等待线程/唤醒等待全部的线程  
    
    Condotion condition = reenstratLock.newCondition();  condition可以精确地指定某些或者说某一个线程的沉睡和唤醒。
    
    threadLocal:每一个线程设置的对象只能够提供给自己的线程使用。线程之间不恭喜。
    
    session里面的设置就使用了threadLocal
    
    单例模式(singleton):无论如何,线程池里面只有一个对象。分为懒汉模式(用到时才new),和饿汉模式(直接new)
    
    concurrent包下的类:
    
    concurrentQueue: 线程安全队列   add() 如果超过容器容量的最大值会抛出异常。
                                    offer() 也是往容器里面加,有返回值boolean类型的,达到最大值的时候它便不能够往里面加了。
    
    concurrentHashMap 和 HashTable 效率问题:concurrentHashMap效率更高。hashTable是锁住整个容器
    
    concurrentHashMap1.7 锁的将容器分成一块一块的segament中的其中一个segament称之为分段锁。
    
    concurrentHashMap1.8 锁的是其中的一个node,采用cas算法来插入或者修改数据。
    
    在你put数据进入concurrentHashMap时,会使用cas算法在内存中计算出一个hash值,如果和你put的对象计算出的hash值一致,则允许写入,保证了原子性。
    
    concurrentSkipListMap :线程安全的有序的map
    
    copyOnWriteList() 写的时候会复制,写的少读得多的情况下使用
    
    linkedBlockingQueue: 同步阻塞容器。 put() 当容器满了之后,线程会自动等待。 无界容器。
    ArrayBlockingQueue:同步阻塞队列 。有界队列,默认有大小,自己也可以设置大小。
    
    delayQueue:可以指定时间,等待时间长的最先执行。
    
    transferQueue: 提供者直接将消息发送给对应的消费者,这样能够提高效率。但是只有transfer()方法会使消息阻塞。
    
    runnable():运行的是run()方法,没有返回值、不能够抛出异常。
    
    callable():运行的是call()方法,有返回值,可以抛出异常。
    
    executors工具类可以创建线程池和task
    
    executorService.submit() 只是将这些任务(task)放入线程池中,但还未真正的运行
    
    // 创建线程池
    ExecuteService service = Executors.newXXXthreadPool();   
    // 将创建好的的任务添加到线程池中,得到一个future类
    Future future = service.submit(callable or implements);
    // future.get() 执行指定线程
    future.get()
    
    线程池demo:
    
    /**
 *  计算1 - 200000 的质数
 */
public class Executor {

    static int myCoreCpu = 4;

    /**
     *  自定义任务
     */
    static class myTask implements Callable{
        int begin;
        int end;

        myTask(int begin,int end){
            this.begin = begin;
            this.end = end;
        }

        @Override
        public List<Integer> call() throws Exception {
            return getPrise(begin,end);
        }
    }

    static List<Integer> getPrise(int from,int end){
        List<Integer> priseList = new ArrayList<>();
        for(int i = from ; i <= end ;i ++){
            boolean prise = isPrise(i);
            if(prise) priseList.add(i);
        }
        return priseList;
    }


   static boolean isPrise(int num){
       for(int i = 2; i < num/2; i++){
           if(num % i == 0){
               return false;
           }
       }
       return true;
   }

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

        List<Integer> l1 = new ArrayList<>();
        List<Integer> l2 = new ArrayList<>();

        /**
         *  不使用线程池做任务调度
         */
        long time = new Date().getTime();
        for(int i = 1;i <= 200000;i++){
            boolean prise = isPrise(i);
            if (prise) l1.add(i);
        }
        long end =  new Date().getTime();
        System.out.println("数组大小:" + l1.size() + "-----用时:" + (end - time));

        /**
         *  线程池实现
         */
        ExecutorService service = Executors.newFixedThreadPool(myCoreCpu);
        myTask m1 = new myTask(1,80000);
        myTask m2 = new myTask(80001,130000);
        myTask m3 = new myTask(130001,170000);
        myTask m4 = new myTask(170001,200000);
        /**
         *  将任务提交到线程池
         */
        Future f1 = service.submit(m1);
        Future f2 = service.submit(m2);
        Future f3 = service.submit(m3);
        Future f4 = service.submit(m4);

        long startTime = new Date().getTime();
        /**
         * 启动线程
         */
        l2.addAll ((List<Integer>)f1.get());
        l2.addAll ((List<Integer>)f2.get());
        l2.addAll ((List<Integer>)f3.get());
        l2.addAll ((List<Integer>)f4.get());

        long endTime = new Date().getTime();

        System.out.println("数组大小:" + l2.size() + "------用时:" + (endTime - startTime));

    }
        /**
         *  关闭service
         */
        service.shutdown();

}

    ExecutorService.submit --> Future --> Future.get();
    
    catchThredPool --> 可以设置线程存活时间aliveTime:默认为60s
    
    Executors工具类里面有很多种类型的threadPool,分别适用于不同的场景,详细的需要看api。
    


    Executors的创建的线程池的底层是 return new ThreadPoolExecutor(int corePoolSise,
        int maxPoolsize,long keepAliveTime,TimeUnit unit,WorkQueue linkedBlockingQueue ThreadFactory tf)
        
    根据里面参数的值可以分为不同类型的线程池:
    
    1.指定核心线程数线程池FixedThread:core = max = 指定参数,keepAliveTime = 0 
        
        特点:使用shoutDown()方法可以线程执行完毕之后自动关闭线程池。
            
        public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
    
    2.单例线程数singletonThreadPool:core = max = 1,keepAliveTime = 0;
    
        特点:线程池中只能有一条线程在执行,其他的线程在workQueue里面等待,能保证线程执行的顺序。
        
        public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
    
    3.缓存线程池,cacheThreadPool: core = 0,max = integer.maxValue(),keepAliveTime = 60L(默认,可自己设定),TimeUnit.SECCOND ,workQueue = SynchronousQueue
    
        SynchronousQueue 是线程安全的queue不经过线程池,直接被调用,如果没人调用就一直处于阻塞状态
    
        public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    4.定时线程:ScheduledThreadPoolExecutor 第三个参数为开启时间,workQueue = delayedWorkQueue;(优先级队列,先进先出)
    
      public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    
    
    
    
    
    
    
    

原创文章 18 获赞 3 访问量 1636

猜你喜欢

转载自blog.csdn.net/qq_39941165/article/details/104308916
今日推荐