线程的创建 有四种方式: 1、实现Runnable接口并重写run()方法 public class StartRun implements Runnable{ public void run() { } } 使用://创建实现类对象 StartRun st=new StartRun(); //创建代理类对象 Thread t=new Thread(st); //启动 开启线程 t.start(); //不保证立即运行,由cpu调用 2、继承Thread类并重写run()方法 public class StartThread extends Thread{ public void run() { } } 使用://创建子类对象 StartThread st=new StartThread(); //启动 开启线程 st.start(); //不保证立即运行,由cpu调用 3、使用Collable并重写call()接口 class Run implements Callable{ //里面是泛型 @Override public Object call() throws Exception { return null; } } 必须使用ExecutorService.submit()方法 调用它 submit会返回一个对象Future,可以使用get()方法来得到该结果 4、使用线程池 ExecutorService exec1=Executors.newCachedThreadPool(); //创建所需数量的线程,在回收线程会停止创建新线程。 ExecutorService exec2=Executors.newFixedThreadPool(4); //有限个线程执行任务 ExecutorService exec3=Executors.newSingleThreadExecutor(); //一个线程 会序列化任务 ExecutorService exec4=Executors.newScheduledThreadPool(4); //参数是核心线程数,每隔一段时间重复执行任务。 还有线程池的参数: corePollSize 核心线程数 queneCapacity 等待队列 MaxPollSize 最大线程数 keepAliveTime 线程空闲时间 keepAliveTime 线程空闲时间 rejectedExecutionHandler 任务拒绝处理器 执行过程 1.当线程数小于核心线程数时,创建线程。 2.当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。 3.当线程数大于等于核心线程数,且任务队列已满。(1)若线程数小于最大线程数,创建线程。(2)若线程数等于最大线程数,抛出异常,拒绝任务。 当多个线程需要访问一个共享数据的时候就需要同步,不然会造成数据出错。 线程同步 使用synchronized关键字同步 <ignore_js_op>![]() 使用Lock加锁 每次得显示的使用Lock,并且使用try finally去关闭Lock,及unLock 使用:Lock lock=new ReentrantLock(); lock.lock(); try{ }finally{ lock.unlock(); } 使用Lock如果出现了异常可以去处理,但是synchronized只会抛出异常,无法处理。 使用volatile关键字 volatile作为java中的关键词之一,用以声明变量的值可能随时会别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效,volatile会禁止指令重排。volatile具有可见性、有序性,不具备原子性。 注意,volatile不具备原子性,这是volatile与java中的synchronized、java.util.concurrent.locks.Lock最大的功能差异。 如果要实现原子性,可以使用原子类AtomicInteger等原子性变量类。 ReentrantLock是一个可重入锁,基本用法和synchronized差不多 <ignore_js_op>![]() 后面java为了优化锁还引入了自旋锁(CAS),轻量级锁,偏向锁等。 <ignore_js_op>![]() <ignore_js_op>![]() 但是CAS可能会出现ABA问题;及如果线程1将值改为B,线程2将值改为A,当线程3去修改值,到底修改了吗? <ignore_js_op>![]() <ignore_js_op>![]() <ignore_js_op>![]() 线程状态 Thread类包含interrupt()方法,因此可以终止阻塞的任务,这个方法将设置线程的中断状态。 可以使用sleep()和wait()方法让线程进入阻塞状态 sleep和wait区别: 1、wait需要notify和notifyAll方法唤醒,sleep里面的参数是毫秒数,让线程阻塞多长时间。 2、wait会让线程释放锁,而sleep不会释放锁 使用notify和notifyAll方法,或者java.util.concurrent类库中等价的signal()或signalAll()方法,线程才会进入就绪状态。 一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才恢复。 在使用锁的时候可能会造成死锁 <ignore_js_op> 预防死锁的就是银行家算法 <ignore_js_op>![]() 总共的资源不仅足够一个线程的需求,还要满足其他线程的需求。 <ignore_js_op>![]() 解决死锁的就是哲学家算法 5个哲学家围着吃饭,每个人中间有一个筷子,每个人必须拿着左右两个筷子才可以就餐,如果每个哲学家先拿起左边的筷子,哪每个哲学家就需要等待其他哲学家释放筷子,造成死锁。 <ignore_js_op>![]() |
|