线程总结

来源:互联网 发布:淘宝幸运大抽奖在哪 编辑:程序博客网 时间:2024/05/17 02:05
 想走的远,那么你基础一定要好。别以为这些不是东西。未来能走多远,要看基础

1,两种开启线程方法:子类,Runnable接口。
                new Thread(){run(){...}}.start();
                new Thread(new Runnable(){run(){...}}).start();


2,定时器Timer,在新的线程里面定时的去做任务(TimerTask接口),设定和运行
                new Timer().schedule(new TimerTask(){run(){...}}, time,times),第一个是要做的任务,用TimerTask封装,第二个是要定的时间,第三个是第二次之后的定时时间。例如,第一次10秒,之后3秒做一次任务,有多少种调用方式,就自己看代码了。

3,线程安全:同步,也就是一些代码只能有一个线程进去,另外的线程想进去,必须等这个线程出来现。
synchronized(对象的锁){},可以修饰代码块,方法,静态方法
synchronized(this){要保护的代码},这里的锁是this,其实可以是什么都行的。在内存的位置是一样的就可以同步了。
块:锁是什么自己写
方法:锁是this
静态方法:锁是类,字节码来的

4,线程通信和互斥:我们想在线程1 在工作的时候,线程2 不要去干预,等线程一工作累了,就告诉线程2 ,线程2 来工作。
这样我们就要用到中间类,new  成final  就可以通信了,之后又同步这个类的方法,只让一个线程进。在这个类加一个判断成员变量就简单多了。通过中间类来完成通信。不是线程管。

5,绑定线程数据:new  一个Map,这样就可以用线程对象多key  ,那么拿值的时候就会不同了,这就是绑定数据。线程结束就删除对应的一条数据就好。 

6,绑定线程数据:ThreadLocal,这个对象相当一个map,set  值就会和线程关联了,只管加入数据。绑定那个数据ThreadLocal  管理。如果想绑定多个数据,就封装一下。要保证ThreadLocal  是唯一的。

7,多个线程访问同一个共享数据,原理:保证数据对象是一个。
  • 将共享数据和操作方法封装在一个对象中,然后将这个对象传递个Runnable  对象(线程要运行的代码就在这个对象里面),这样就容易实现数据的互斥和通信。最好就加上同步。
  • 利用内部类访问外部成员:把要处理的数据当作类的属性,处理方法封装好,记得同步,把Runnable  继承成内部类,那么run  方法就可以访问了。
 ------以上是java1.4 以前版本的写法,下面是1.5 的 -----


8,线程池ThreadPool:这个人,是管理线程去做任务的,做任务的中介吧,如果人家有任务(Runnable )就给这个线程池,给完就可以回去了,中介是有工厂或人力的(线程Thread),看看谁有空,有空就叫去做任务,如果全部都在忙,那么这些任务就会在队列里面排队,是异步处理的。做网站服务器类似的就会用到。
  • ThreadPool  线程池
    1. ExecutorService threadPool = Executors.newFixedThreadPool(3);  固定的工人,就是固定的线程
    2. ExecutorService threadPool = Executors.newCachedThreadPool();  有缓冲的,有多少任务就请多少工人
    3. ExecutorService threadPool = Executors.newSingleThreadExecutor();  单个,解决线程死掉怎么救活
    4. 任务 Runnable
      • threadPool.execute(new Runnable(){要做的任务});  这个方法在主线程调用,加入任务的。
      • 工具类  Executors

      9,线程返回结果Future:当一个线程结束之后就会返回一个结果给我们。
      • 在线程池用submit()提交任务,这个时候任务是Callable  ,类似Runnable,运行函数为 call()  有返回结果的。
      • Future future =threadPool.submit(new Callable() {call(){...}};
      • 之后再调用 future.get()拿到返回结果的。但不知道什么时候有返回结果,感觉没有什么用咯,还不如自己做任务,你帮我做任务,还要我看着。
      10,提交多个任务CompletionService  :new 的时候要传进线程池,也是用submit()提交任务。用completion.task().get();拿到返回的结果,一次拿一个,


      11,java5中的锁Lock : 就是同步,进入一段代码的时候,锁上,在出来的时候,开锁,这样就不怕的被打断了。
      • Lock lock = new ReentrantLock();  //Lock  是接口,要new  其子类,Ctrl + T 一下就知道了。
      • lock.lock();  //上锁
      • try{......}finally{lock.unlock();} //开锁,要try/finally

      12,读写锁ReadWriteLock : 可以上读或写的锁,读与读不互斥,读与写互斥,写与写互斥。
      • rw.readLock().lock();
      • rw.readLock().unlock();
      • rw.writeLock().lock();
      • rw.writeLock().unlock();
      13,线程交换工作:就是你一下,我一下的工作,这个思想是怎么样呢。不是自己工作就wait(),自己做完了就notify(),叫醒别人。
      • 在通信的中,共享的数据封装在相对线程是外部的,有了一个唯一的数据对象data。
      • data有两个方法,分别给不同的线程调用m1()和m2();这两个方法会加Lock的。
      • 一开始是线程一调用m1(),在线程机制中,每一个分配的时间是由CPU控制的,不能设置,那么现在是线程一工作,就要用状态变量标识bShouldSub,
      • 当线程一工作完了,就唤醒线程二,但本应该到线程二工作了,但CPU还不急让线程二工作,时间还是给了线程一,这个时候就应该让线程一wait(),这样线程二就可以工作了。
      • 重复上面的过程,就是可以,你一下,我一下的工作了。

      • 完成这个,完成上面的通信,是在锁住的时候通信的,就是Lock里面的通信Condition,Lock.Condition
      • 生成锁,lock = new ReentrantLock();
      • 拿到这个锁的通信,Condition condition =   lock. newCondition();  
      • 用condition.await()  代替  this.wait();
      • 用condition.signal(); 代替  this.notity();    , 同一个signal(),  只能叫醒用同一个对象await() 的对象。
      • 这样就可以了。称为:多个Condition通信,可以很多个Condition

      • 从两个封装中更加能体现面向对象的思想。用Condition可以实现诸塞队列。
      14,灯的互斥Semaphore:信号 vt. 用信号联络  vi. 发信号,这个人是管理工作的人数,只有有灯的线程才能去工作,不然就wait()
      • sp.acquire();  获得灯,如果没有灯就等,等,可以做排队用。有多少 队 ,就有多少灯
      • sp.release();  做完任务就还灯。

      15,线程集合一起行动:CyclicBarrier,在new  的时候就会告诉这个人,我们又多少个线程要去工作,如果线程调用这个类的await()  方法,说明到达了集合地点,那么要等够一开始设置的线程数量才能一起出发,这个等待是自己动开始的,只要人数够了,就可以行动。
      • final  CyclicBarrier cb = new CyclicBarrier(3);
      • cb.await();// 一定要等够线程调用这个方法才能走下去。
      16,计数器:CountDownLatch , 给我看清楚了,这个是计数器,不是计时器,是用来算数字的,一开始设置有多少个数,一次减去1 ,直到零,如果有线程用这个对象来await(),  那么就可以开始往下走。同一对象原则。互动对象原则
      • 应用1 :设置计数为1 ,有一个准备时间,到时间减1 开始行动,就像起跑线上的。
      • 应用2 :设置计数为总人数 n,每一个人做完工作就减1 ,当全部人做完工作,刚好减为 0 ,就可以去统计什么的,就像都跑到了终点。
      • final CountDownLatch cdOrder = new CountDownLatch(1);
      • final CountDownLatch cdAnswer = new CountDownLatch(3);
      • cdOrder.await();
      • cdOrder.countDown();
      • 一对多,多对一
      17,线程交换数据:Exchanger,连个线程交换数据,谁先到交换地点谁就等一下,调用了一个方法exchange(data), 就是到达了,当两个人都到了的时候就交换,之后就向下走了。可以换不同的数据
      • final Exchanger exchanger = new Exchanger();
      • Ex data2 = (Ex)exchanger.exchange(data1);
      18,阻塞队列:BlockkingQueue,想知道怎么用就看API去,可以和线程池结合使用。
      • 放满之后就在等待,等有人拿走了才能放进去。

      19,并发集合: ConcurrentSkipListMap等等,要用就是查,我这里只是讲几个。
      • 之前的集合对线程是不安全的。读的时候就不要写,写的时候就不要读。就想lock  一样,有异常这些东西的。只是原本Collection  的毛病。
      • CopyOnWriteArrayList  这个类在写的时候会Copy  一份,就不会有事,
      20,同步队列:SynchronousQueue,也是一个阻塞队列,这个队列是,当有人来读的时候才插入数据。如果有多个人来读数据,就插进去,看谁抢的快,谁就拿呗。

      问题:可以不可以,主线程调用线程1 下载数据,主线程在做自己的事情,当线程1 下载完数据之后主线程就拿到下载后的数据输出。就是没有等待的时间。以上讲的都是异步线程同步通信的。




      线程要做的东西:
      1. 做任务:
        • 线程池最好,把任务一丢就行了。任务是Runnable
        • 阻塞队列也行,自己new  Thread,任务是什么都行,如果想线程多次拿队列的东西,就 while(true){...}
        • 排队消费:有线程,队列,灯
          • 只有一盏灯,只有拿到灯的才能消费。
          • 用同步队列,只有有人来拿数据才插入数据
          • 用new  产生线程,用来消费
          注意:

          1. 线程池的工作是做任务,只管做完,在做完的时候也可以返回结果,但这样的结果没有什么用,和通信有很大的差别,通信可以一做一下,我做一下,但线程池,你给任务,我就一下子去做完。

          看完帮忙评论一下,一起进步嘛。
          0 0