多线程并发基础

1.daemon 线程,守护线程是在后台为用户线程提供服务的,一旦守护线程监听到用户线程全部执行完,则直接结束,而不会保证自己的代码执行完
2.守护线程的设置setDaemon(true),必须在start之前调用,否则保存,守护线程不应该做一些逻辑较多的业务,比如读数,因为无法保证操作完,好的例子GC
3.线程的退出和终止,以前用stop,但是由于stop会强行结束线程,会导致线程释放锁,数据不安全,现在用interrupt,只是向线程发出终止信号,如果不响应这个信号,那线程也不会结束 ,一般用isInterrupt判断,return结束
isInterrupt不会清除终止标志位,interrupted清除中断标志位,另外sleep方法报错interruptEx时return也可以结束线程,并清除标志位
当我们调用线程的interrupt方法,它有两个作用:
1、如果此线程处于阻塞状态(比如调用了wait方法,io等待),则会立马退出阻塞,并抛出InterruptedException异常,线程就可以通过捕获InterruptedException来做一定的处理,然后让线程退出
2、如果此线程正处于运行之中,则线程不受任何影响,继续运行,仅仅是线程的中断标记被设置为true。所以线程要在适当的位置通过调用isInterrupted方法来查看自己是否被中断,并做退出操作
4.sleep和yield的区别,sleep可以设置时长,yield不能,另外yield只能给优先级高的分配cpu,sleep不管优先级,都能分
5线程优先级,setPriority(),范围1-10,数值越大,越优先被执行的概率越大,但是不能保证同步两个线程,并且不同系统上设置的优先级效果可能不一样的,这取决于操作系统,总的来说,优先级高的线程获得cpu资源的概率较大
6join方法会使得调用者的主线程阻塞,直到这个调用者执行完之后,再继续执行,比如在main中写t.join,则线程t会执行完,然后main才会执行,如果在主线程总有两个导致死锁的线程,通过join方法,能够使得一个线程执行完之后,主线程再去启动另一个线程,也能实现线程顺序执行。
7.wait和notify是object的方法,必须都先获得对象锁,即每次使用都要加synchronized,然后在同步代码中使用,如果不拥有对象的监视器(锁),则会报错iiegalmonitorstateEx。
https://blog.csdn.net/qq_41140741/article/details/81488663
8.线程的信号灯可以用于控制获取资源的的线程数量,5就表示获取资源的线程数最大为5,final Semaphore sp = new Semaphore(5); //单个信号灯;调用sp.acquire(),进行控制,通过sp.release可以放开一个通行证,让其他的线程进来拿到通行证,即获取资源的使用权
9ArrayList不安全,并发操作(add等)会出现不可预知的结果,Vector操作安全,集合中可以通过Collections.synchronizedList(new ArrayList());或者
List list=Collections.synchronizedList(new ArrayList());
Set s=Collections.synchronizedSet(new HashSet());
Map m=Collections.synchronizedMap(new HashMap());
10.读写锁的使用,读读共享,读写互斥,写写互斥,不过如何只将锁加在对象上,实现对对象的读用读锁,对对象的写用写锁呢??,synchronized是将锁加在对象上的,jvm自动释放锁,且发生异常时也自动释放锁;lock需要手动释放,异常时不释放,所以一般写在finally块中

     final ReadWriteLock readWriteLock  =new ReentrantReadWriteLock();
        final List<Integer> stringBuffer= new ArrayList<>();
        //读锁

        Thread thread1= new Thread("read"){

            @Override
            public void run() {
                readFile(stringBuffer,readWriteLock.readLock());
            }
        };



        //写锁
    Thread thread2= new Thread("write"){
        @Override
        public void run() {
            readFile(stringBuffer,readWriteLock.writeLock());
        }
    };

    thread1.start();

   thread2.start();


}
    private  static void readFile(List<Integer> sb,Lock lock){

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName());
            System.out.println("开始写文件。。。。");
            for (int i = 0; i <10; i++) {
               sb.add(i);
            }
            System.out.println(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

11.ThreadLocal创建线程私有变量数据的副本,原理是get,set和remove方法都维护了一个ThreadLocalMap,使用entry<T:T>,以当前线程为key,以set的值为value,每次get时直接在ThreadLocalMap.get(当前线程),获得当前线程存的值, https://zhuanlan.zhihu.com/p/34494674 ,entry中的key为弱引用,使用完GC会自动回收,但是value是强引用,除非手动value=null,否则不会回收,容易内存泄露,应该使用完threadLocal之后调用remove(),用法简单示例:

static  Integer integer = 0;
    static  ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
@Test
    public static void main(String[] args) {

    //for (int i = 0; i <10 ; i++) {
        final String resouce1 = "线程-" + 1;
        final String resouce2 = " value = (" + 1 + ")";
      Thread thread =   new Thread(resouce1){
             @Override
             public void run() {
                 integer++;
                //System.out.println("第二次"+integer);
                 threadLocal2.set(integer);
                 System.out.println("第一次"+threadLocal2.get());
                  threadLocal2.remove();
                 
             }
         };
      Thread thread1 =   new Thread(resouce2){
             @Override
             public void run() {
                 integer++;
                 //System.out.println("第二次"+integer);如果单纯打印未同步的interger则会出现两个线程打印相同的数据,原因是thread1还没让interger++,thread就已经打印了
                 threadLocal2.set(integer);
                 System.out.println("第二次"+threadLocal2.get());
                  threadLocal2.remove();
             }
         };

    thread1.start();
    thread.start();

12.为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。线程池能够节省大量开启和销毁线程的开销,线程池用完之后一定要shutdown()关闭,否则线程池资源一直不释放,jvm无法关闭;shutDown()拒绝任务的提交,但是会将队列中的任务执行完毕,shutDownNOw()会直接结束线程池,其中的任务全部中断
13.四种java并发包中提供好的线程池:
//固定数量的线程池
static ExecutorService fixedThreadPool1 = Executors.newFixedThreadPool(10);
//如果有线程空闲,就会重复使用,但是极端情况下容易出现多少个任务就建多少个线程,不好控制
static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//单线程池,能保证执行的任务按执行顺序
static ExecutorService fsingleThreadPool = Executors.newSingleThreadExecutor();
//调度线程池,能够按照一定频率,在延迟多久之后执行定时计划,可以做定时任务
static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
14.已有的线程池底层都是用ThreadPoolExecutor写的
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue);
ThreadPoolExecutor参数讲解:https://zhuanlan.zhihu.com/p/43118842
讲解 https://www.jianshu.com/p/c41e942bcd64
15.submit和execute的区别:
(1). 这个submit方法返回值为null
Future future = fixedThreadPool1.submit(new ThreadTest(“线程”+i));
(2).这个submit可以指定返回值的类型(即第二个参数的类型),Future没什么特殊的方法 ,就是常用get获得返回值
(3).Future future = fixedThreadPool1.submit(new ThreadTest(“线程”+i),“线程”+i+“的返回值”);
(4)submit返回值由Callable中类型决定
Future future = fixedThreadPool1.submit(new CallableTest(“线程” + i));
(5).总结submit和execute的区别:1.execute执行任务遇到错误会直接抛出来,但是submit会先将异常吞掉,不会抛;
2.submit有返回值,根据不同的重载方法返回不同值,submit(Runnable)返回null,submit(Runnable,T t),返回值为T的类型的t,execute没有返回值
3.返回值能通过Future接受,并通过Futer.get方法获得返回值
16.重入锁:当前线程已经获得了锁以后仍能够进入该锁的同步代码块中,参考
https://blog.csdn.net/u012545728/article/details/80843595, 不可重入锁则是线程一旦获得了锁之后就不能重复进入同步代码块了,java中的ReentrantLock和和synchronize都是重入锁
17.阻塞队列BlockingQueue,典型的线程中生产者和消费者模式,offer是生产,先进去的先被poll消费,先生产的会在队首首先被消费 eg:

//生产者//消费者

  BlockingQueue blockingQueue = new ArrayBlockingQueue(10);
    Producer producer1 = new Producer(blockingQueue,"producer1");
    ....
   // //开始生产,最长阻塞2秒,2秒内生产了则为true,否则为false
    //  boolean isr  =blockingQueue.offer(this.name,2, TimeUnit.SECONDS);
    //开始消费,有则直接返回队首结果,没有就阻塞两秒,超过两秒则返回null,
   // String data  =blockingQueue.poll(2,TimeUnit.SECONDS);

18.synchronized这个锁的原理synchronized这个锁的原理
https://blog.csdn.net/javazejian/article/details/72828483
19.主线程不会等待子线程执行的结果,而是只要主线程执行完就会返回值,页面请求也是如此

发布了88 篇原创文章 · 获赞 5 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_42410730/article/details/92579191