java基础2
1.如何实现list和数组相互转化:
- 数组转List
1).使用for循环将数组元素加入List中;
List<String> list=new ArrayList<String>();
for (String string : str) {
list.add(string);
}
2).使用asList()方法,注意:asList()返回的列表大小是固定的,并且不是java.util.ArrayList类,而是定义在java.util.Arrays中一个私有静态类java.util.Arrays.ArrayList,这种情况,如果添加或者删除列表中的元素,程序会抛出异常
List<String> list3 = Arrays.asList(str);
3).使用Collections.addAll()
List<String> list4=new ArrayList<String>(str.length);
Collections.addAll(list4, str);
4).使用Stream中的Collector收集器
List<String> list5=Stream.of(str).collect(Collectors.toList());
- List转数组
1).使用for循环
String[] str1=new String[list.size()];
for(int i=0;i<list.size();i++) {
str1[i]=list.get(i);
}
for (String string : str1) {
System.out.println(string);
}
2).使用toArray()
String[] str2=list.toArray(new String[list.size()]);
2.Array和ArrayList区别
- 存储内容:Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。注意:Array数组在存放的时候一定是同种类型的元素;ArrayList就不一定了,因为ArrayList可以存储Object
- 空间大小:Array大小是固定的,ArrayList的大小是动态变化的。
- 方法:ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
- 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
- 基于效率和类型检验,应尽可能使用Array,无法确定数组大小时才使用ArrayList!不过当你试着解决更一般化的问题时,Array的功能就可能过于受限。
3.迭代器Iterator
- 迭代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类,它是一个很典型的设计模式。Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
4.并发和并行的区别
- 并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
- 并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
- 并发是指在一段时间内宏观上多个程序同时运行。并行指的是同一个时刻,多个任务确实真的在同时运行。
- 并发的多个任务之间是互相抢占资源的;并行的多个任务之间是不互相抢占资源的。
5.线程与进程的区别
- 进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
- 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
- 一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务。
6.守护线程(Daemon Thread)
- 只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
- Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
7.线程有哪些方式
-
新建(NEW):新创建一个线程对象
-
就绪:一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。
-
运行状态(running):当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。
-
阻塞状态(blocked):线程运行过程中,可能由于各种原因进入阻塞状态:
①线程通过调用sleep方法进入睡眠状态;
②线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
③线程试图得到一个锁,而该锁正被其他线程持有;
④线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
-
死亡状态(dead):有两个原因会导致线程死亡:
①run方法正常退出而自然死亡;
②一个未捕获的异常终止了run方法而使线程猝死;
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。
image.png
8.sleep()和wait()的区别
- sleep() 方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。
因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象
sleep可以在任何地方使用 - wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程
wait只能在同步控制方法或者同步控制块里面使用
9.notify和notifyAll区别
- 锁池和等待池
锁池:假设线程a已经拥有某个对象的锁,而其他的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须获得该对象的锁的拥有权,但是该对象的锁目前正被线程a所拥有,所以这些线程就进入了该对象的锁池中
等待池:假设一个线程a调用了某个对象的wait()方法,线程a就会释放giant对象的锁后,进入到该对象的等待池中,等待池中的线程不会去竞争该对象的锁 - 区别:当有线程调用了该对象的notifyAll()方法(该方法唤醒所有wait线程)或者notify()方法(只随机唤醒一个wait线程),被唤醒的线程就会进入该对象的锁池中,锁池中的线程就会竞争该对象锁。即调用了notify后只有一个线程会由等待池进入锁池,而notifyAll会将该对象等待池中所有线程移动到锁池中,等待锁竞争
- 永远在循环(loop)里调用 wait 和 notify,不是在 If 语句
10.java创建线程池的方式
- Executors目前提供了5种不同的线程池配置
- 1.newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。
- 2.newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads。
- 3.newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目。
- 4.newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
- 5.newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。