java内功系列三(多线程)

1.并发和并行区别:并行指某时刻多个同时执行,并发指某时刻只能有一个执行但是由于cpu的轮换执行宏观上感觉是同时执行。

2.线程拥有自己的堆栈、程序计数器、局部变量。但是多个线程只能共享父进程的全部资源。

3.线程启动几种方式。一:继承Thread后重写run方法后就可以实例化对象用star()启动一个线程执行(多个thread之间不能共享)。二:实现Runnable(多个thread启动时传入同一个Runnable对象,可以实现对象共享),在此接口也有一个run方法,在new thread时候最为参数传递给构造函数,最后也是有star()启动。三:callable(call方法有返回值并可以声明异常抛出,它还是函数式接口所以能用lambda)接口,future接口代表callable方法返回值,其中future能对正在执行的线程进行监控。future有个实现类futuretask还实现了runnable接口,因此可以用它包装callable对象然后在作为线程的target在启动。如:
        //用lambda表达式创建一个callable对象,然后用FutureTask包装作为线程target
        FutureTask<Integer> task=new FutureTask<>((Callable<Integer>)()->{return 1;}) ;
        new Thread(task,"有返回值").start();
        //用task可以查看线程执行情况进行下一步操作,如果线程抛出异常,可以进行捕获
        try {
            if(task.isDone())
            task.get();//线程执行返回值,如果线程还没有执行完此处或阻塞
        } catch (ExecutionException e) {            
            e.printStackTrace();
        }
采用runnable、callable接口方式实现好处是实现的是接口,因此实现类还可以继承或者实现其他接口,当处理相同任务时可以多个线程用同一个对象,实现线程间资源共享。

4.线程状态:新建、就绪、运行、阻塞、死亡。以下是线程各个阶段情况重点看阻塞如下:

5.线程工具:join(阻塞当前线程等待其他线程执行完后再执行),setDaemon(设置当前线程是否为守护线程),yield(不建议使用。当前线程为就绪状态,让调度器重新调度一次,和sleep不一样。),setPriority(设置线程优先级)。

6.线程同步:
使用同步代码块(在{}中的代码安全):
synchronized (obj) { //需要同步的代码}
同步方法,也就是用synchronized修饰方法。

7.释放同步监视器的锁:程序正常或者异常结束导致程序不会在执行。调用了同步监视器的wait方法。以下情况不会释放:Thread.sleep、Thread.yield、suspend挂起线程。

8.同步锁:Lock和ReadWriteLock是锁的2个根接口。分别提高了重入锁ReentrantLock和ReentrantReadWriteLock。java8提供StampedLock,大多数可以代替ReentrantReadWriteLock。
使用锁比同步代码块和同步代码方法好处有几点:灵活不用在固定代码块,前面方式加锁如果出现多次加锁需要按照顺序解锁
    private final ReentrantLock lock=new ReentrantLock();
    public void m()
    {
        lock.lock();//加锁
        try
        {
            //安全代码
        }
        finally {
            lock.unlock();//释放锁,必须释放
        }
    }


9.线程之间的通信:针对用synchronized进行同步的方法或者代码块使用wait(对当前线程挂起并释放锁),notify(通知单个挂起的线程可以继续了),notifyall(通知所有的线程可以继续访问),调用上述3个方法的对象是对应的锁对象。针对用lock对象实现的同步方法采用condition对象。condition(lock.newCondition方法获取)对象类似上述锁对象,提供了对应的await(),signal(),signalall()3个方法。和全面三个方法一一对应。

10.针对生产者和消费者java5提供了blockingqueue接口,对消费者和生产者进行了控制,当容量为空消费者阻塞,当容量满时生产者阻塞。提供了多个实现:


11.给线程设置异常处理类(默认情况在线程组中已经实现了线程异常处理方法,所有的线程都有所属组):
        Thread d;
        d.setUncaughtExceptionHandler(eh);//设置单个线程实例的异常处理类
        d.setDefaultUncaughtExceptionHandler(eh);//设置默认的该类所有实例的线程异常处理类

12.线程池使用executors工厂类提供的几个静态方法创建。
        //返回ExecutorService(提供submit方法,可以传递runnable或者callable,返回future对象)
        Executors.newCachedThreadPool();//返回缓存线程池
        Executors.newFixedThreadPool(arg0);//返回固定个线程线程池
        Executors.newSingleThreadExecutor();//返回单个线程池
        
        //返回ScheduledExecutorService(提供Schedule方法,可以传递runnable或者callable并带上延迟参数和周期调用参数,返回Scheduledfuture对象),
        //线程池具有调度功能,可以延迟调用或者周期调用
        Executors.newScheduledThreadPool(arg0);//
        Executors.newSingleThreadScheduledExecutor();
        
        //返回ExecutorService,他是针对硬件内核实现并行计算
        Executors.newWorkStealingPool(arg0);
        Executors.newWorkStealingPool();//根据内核个数返回能并行计算的线程池
例如:
        ExecutorService pool=Executors.newFixedThreadPool(6);
        Runnable target=()->{
            for(int i=0;i<100;i++)
            System.out.println(Thread.currentThread().getName());
        };
        pool.submit(target);
        pool.submit(target);
        //关闭线程池
        pool.shutdown();

13.为了充分利用CPU多核因此java7提供ForkJoinPool(继承至ExecutorService,也是线程池)把大任务拆分执行。提供了2个构造方法和有一个类静态方法构建ForkJoinPool。构建完后可以传递ForkJoinTask对象给ForkJoinPool开始执行任务。ForkJoinTask是Future的子类,并且有2个子类RecursiveAction(没返回值)和RecursiveTask(有返回值)。

    //继承RecursiveTask实现可分解任务(带返回值,RecursiveAction不带返回值)
    class CalTask extends RecursiveTask<Integer>
    {
        //每个小任务最多累加到20个数
        private static final int Threshold=20;
        private int arr[];
        private int start;
        private int end;
        //累加从start到end的数组
        public CalTask(int[] arr,int start,int end)
        {
            this.arr=arr;
            this.start=start;
            this.end=end;
        }
        //继承都要重写这个
        @Override
        protected Integer compute() {
            int sum=0;
            //裁缝任务模式
            if(end-start<Threshold)
            {
                for(int i=start;i<end;i++)
                {
                    sum+=arr[i];
                }
                return sum;
            }
            else
            {
                //进一步拆分
                int middle=(start+end)/2;
                CalTask left=new CalTask(arr, start, middle);
                CalTask right=new CalTask(arr, middle, end);
                //继续执行
                left.fork();
                right.fork();
                //因为要返回值,所有有这句,如果是RecursiveAction就不需要这句
                return left.join()+right.join();
            }
        }
    }
    public class Sum
    {
        public static void main(String[] args) throws Exception
        {
            int[] arr=new int[100];
            Random rand =new Random();
            int total=0;
            //开始初始化
            for(int i = 0 , len=arr.length;i<len;i++)
            {
                int tmp=rand.nextInt(20);
                total+=(arr[i]=tmp);
            }
            System.out.println(total);
            ForkJoinPool pool=ForkJoinPool.commonPool();
            //因为也是线程池返回一个Future对象
            Future<Integer> future=pool.submit(new CalTask(arr, 0, arr.length) );
            System.out.println(future.get());
            //关闭线程池
            pool.shutdown();
        }
    }


14.threadlocal可以把变量当做副本进行包装。针对以前的集合使用collections的静态方法可以把非线程安全的集合包装成线程安全的集合。

15.java针对结合重新设计了一套新的线程安全结合,以Concurrent和CopyOnWrite开头。
        //ConcurrentHashMap<K, V>
        //ConcurrentLinkedDeque<E>
        //ConcurrentLinkedQueue<E>
        //ConcurrentSkipListMap<K, V>
        //ConcurrentSkipListSet<E>
        
        //CopyOnWriteArrayList<E>
        //CopyOnWriteArraySet<E>


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值