多线程 1、java实现线程有哪几种方式
1.继承Thread类实现多线程
2.实现Runnable接口方式实现多线程
3.使用线程池:如ExecutorService,Callable,Future
第一种和第二种方式相比:java类只能允许继承一个父类,可以实现多个接口;其次,在第一种方式中用this可以获取当前线程,第二种方式获取当前线程只能通过Thread.currentThread()
第三种:线程的创建和释放,需要占用不小的内存和资源。如果每次需要使用线程时,都new 一个Thread的话,难免会造成资源的浪费,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪, ExecutorService是Java提供的线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
corePoolSize:核心线程数
maximumPoolSize:最大线程数
poolSize:线程池中当前线程的数量
1、1那么poolSize、corePoolSize、maximumPoolSize三者的关系是如何的呢?
当新提交一个任务时:
(1)如果poolSize<corePoolSize,新增加一个线程处理新的任务。
(2)如果poolSize=corePoolSize,新任务会被放入阻塞队列等待。
(3)如果阻塞队列的容量达到上限,且这时poolSize<maximumPoolSize,新增线程来处理任务。
(4)如果阻塞队列满了,且poolSize=maximumPoolSize,那么线程池已经达到极限,会根据饱和策略RejectedExecutionHandler拒绝新的任务
ExecutorService分为以下几种线程池:可缓存线程池newCachedThreadPool(),其中创建的都是非核心线程,;单线程newSingleThreadExecutor();
固定线程池newFixedThreadPool(3);定时线程池
2、线程同步有哪几种方法
Synchronized,Lock锁,分布式锁
Synchronized,Lock锁的区别:
1.Synchronized是java内置关键字,Lock是java 类 Lock lock = new ReentrantLock()
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取锁lock.tryLock()
3.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平
4.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题
5.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
6.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了//lock.tryLock(3000,
TimeUnit.MILLISECONDS) //尝试获取锁 获取不到锁,就等3秒,如果3秒后还是获取不到就返回false
3、synchronized与volatile区别
同步块(或方法)和 volatile
变量。这两种机制的提出都是为了实现代码线程的安全性。其中
Volatile
变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。volatile关键字用于声明简单类型变量,如int、float、boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n
= m +
1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。
4、wait()与sleep()的区别
(1)sleep()来自Thread类,和wait()来自Object类.调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
(2)sleep()睡眠后不出让系统资源,wait让其他线程可以占用CPU
3)sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒.而wait()需要配合notify()或者notifyAll()使用
5、什么是乐观锁和悲观锁
1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因而它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,假如失败则表示发生冲突,那么就应该有相应的重试逻辑。
2)悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因而每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不论三七二十一,直接上了锁就操作资源了。
6、线程有哪些状态 (生命周期)
1创建
new线程对象 仅仅在堆内存上有一个线程对象
2就绪状态
start方法之后 已经准备好可以运行 但是经过CPU的调度 等待CPU的执行
3运行状态
CPU正在执行该线程的任务
4阻塞状态
因为一些原因 线程要暂时停止任务
等待从阻塞状态解除后继续执行 (造成阻塞join、wait()、sleep()等 解除阻塞:sleep睡眠时间到、notify()唤醒wait()、join加入线程结束)
5死亡状态
线程任务结束 线程对象占用的内存被回收
7、线程和进程的区别
1根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
2在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
3所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
4内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
5包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
8、线程常用的API
(1)Join方法 加入当前线程的方法 阻塞被加入的线程 等待加入的线程执行完毕才回来继续执行被加入的线程
(2)setPriority() 设置优先级的方法
(3)setName() 设置线程名的方法
(4)Thread.sleep() 睡眠方法 让当前线程沉睡****毫秒
当前线程就会处于阻塞状态 等到时间到了 就会恢复到就绪状态
(5)Thread.currentThread();获得当前线程对象的方法
(6)Thread.yield() 退让方法 执行了yield方法 那么当前线程就会从CPU执行状态恢复到就绪状态
(7)setDaemon(true);设置伴随线程的方法 守护线程 一个线程伴随着另一个线程一同运行 当被伴随的线程结束 伴随线程一同结束