Java核心技术----并发

  多进程与多线程的区别:每个进程拥有自己的一整套变量,而线程则共享数据

   线程:Thread(Runnable target) //构造一个新的线程,用于调用给定目标的run()方法

              void start() //启动线程

              Runnable r=()→{ task code};//用lambda表达式建立一个实例

              Thread t=new Thread(r);

              t.start();

  线程状态:New(新创建)

                    Runnable(可运行)一旦调用start方法,线程就处于可运行状态,一个可运行的线程可能正在运行也可能没有运行

                    Blocked(被阻塞)当一个线程试图获取一个内部对象锁,而该锁被其他线程持有,则该线程进入阻塞状态

                    Waiting(等待)调用Object.wait方法或Thread.join方法

                    Timed waiting(计时等待)调用Thread.sleep方法

                    Terminated(被终止)run方法正常退出;因没有捕获的异常终止了run方法

线程优先级:每一个线程有一个优先级,默认下,一个线程继承它的父线程的优先级

                      setPriority(int newPriority)可以设置优先级

守护线程:为其他线程提供服务,若只剩下守护线程,虚拟机就退出了

                  t.setDaemon(true);//将线程设置为守护线程

未捕获异常处理器:在线程中无法捕获异常,通常将异常传递到一个用于未捕获异常的处理器;该处理器必须属于一个实现了Thread.UncaughtExceptionHandler接口的类

同步:防止代码块受并发访问的干扰

有两种方法:1)lock/Condition     private Lock bankLock=new ReentrantLock();//创建重入锁对象

private Condition sufficientFunds=bankLock.newCondition();//创建条件对象:用于管那些已经获得了一个锁却不能做有用工作的线程

bankLock.lock();

try{

         while(accounts[from]<amount)

         sufficientFunds.await();//线程被阻塞,并放弃了锁

          ....

         sufficientFunds.signalAll();//重新激活因这一条件而等待的线程

       }

  finally{

           bankLock.unlock();

       }

         2)Synchronized声明   每一个对象有一个内部锁,并且该锁有一个内部条件

                          public synchronized void method(){ }

                          /  synchronized(obj){ }

对于这两种方法使用的建议:最好既不用lock/Condition,也不用Synchronized,而采用java.util.concurrent包中的一种机制

Volatile域:为实例域的同步访问提供了一个免锁机制,但volatile变量不能提供原子性。

原子性:对数据的操作是一个独立的、不可分割的整体,即一次操作是一个连续不可中断的过程,数据不会执行一半的时候被其他线程所修改。

死锁的四个必要条件:1)互斥条件:即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。

                                    2)不可抢占条件:进程所获得的资源在未使用完毕之前,资源申请者不能强行的从资源占有手中夺取资                                              源,而只能由该资源的占有者进程自行释放。

                                    3)占有且申请条件:进程至少已经占有一个资源但又申请新的资源。

                                    4)循环等待条件:存在一个进程等待序列{P1...Pn},其中P1等待P2所占有的某一资源,...,Pn等待P1

                                          预防死锁即破坏四个必要条件之一。

阻塞队列:常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程,当队列为空时,获取元素的线程会等待队列变为非空,当队列满时,存储元素的队列会等待队列可用。

         void put(E element);//添加元素,在必要时阻塞

         E take();//移除并返回元素,必要时阻塞

常见的阻塞队列:ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue...。java.util.concurrent包提供了线程安全的集合:ConcurrentHashMap/ConcurrentSkipListMap/ConcurrentSkipListSet/ConcurrentLinkedQueue

Callable和Future:提供有返回值且能抛出异常的异步方法。通过Callable的call()产生结果,Future的get()拿到结果。

FutureTask包装器可以将Callable转换成Future和Runnable。

           Callable<Integer> myComputation=...;

           FutureTask<Integer> task=new FutureTask<Integer>(myComputation);

           Thread t=new Thread(task);

           t.start();

           ...

           Integer result=task.get();

线程池:当程序中创建了大量的生命周期很短的线程,应该使用线程池,一个线程池中包含许多准备运行的空闲线程。将Runnable对象交给线程池,会有一个线程调用run方法,当run方法退出时,线程不会死亡,而是池中准备为下一个请求提供服务。它能减少并发线程的数目。

               ExecutorService pool=Executors.newCachedThreadPool();//调用Executors中的静态方法创建线程池
               MatchCounter counter=new MatchCounter(new File(directory),keyword,pool);//MatchCounter实现了Callable接口的类
               Future<Integer> result=pool.submit(counter);//调用submit提交Runnable或Callable对象,返回Future对象

               pool.shutdown();//当不再提交任何任务时,关闭

Fork-Join框架:用于并行执行任务的框架,将大任务分割成若干小任务,最终汇总每个小任务结果后得到大任务结果的框架。

                         Counter counter=new Counter(numbers,0,numbers.length,x-> x>0.5);//Counter继承了RecursiveTask<Integer>
                         ForkJoinPool pool=new ForkJoinPool();
                         pool.invoke(counter);
                         System.out.println(counter.join());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值