目录
3、高并发、任务执行时间短的业务怎么使用线程池?并发布高、任务执行时间长的业务怎么使用线程池?并发高业务执行时间长的业务怎么使用线程池?
7、并发线程(concurrency)并发编程(parallellism)有什么区别?
1. 进程是系统进⾏资源分配的基本单位,有独⽴的内存地址空间2. 线程是 CPU 独⽴运⾏和独⽴调度的基本单位,没有单独地址空间,有独⽴的栈,局部变量,寄存器, 程序计数器等。3. 创建进程的开销⼤,包括创建虚拟地址空间等需要⼤量系统资源。4. 创建线程开销⼩,基本上只有⼀个内核对象和⼀个堆栈。5. ⼀个进程⽆法直接访问另⼀个进程的资源;同⼀进程内的多个线程共享进程的资源。6. 进程切换开销⼤,线程切换开销⼩;进程间通信开销⼤,线程间通信开销⼩。7. 线程属于进程,不能独⽴执⾏。每个进程⾄少要有⼀个线程,成为主线程。
1. 继承 Thread 类,重写 run ⽅法2. 实现 Runnable 接⼝,重写 run ⽅法,实现 Runnable 接⼝的实现类的实例对象作为 Thread 构造函数的 target3. 实现Callable接⼝通过FutureTask包装器来创建Thread线程4. 通过线程池创建线程
public class ThreadDemo03 { public static void main(String[] args) { Callable FutureTaskoneTask = new FutureTask (oneCallable); Thread t = new Thread(oneTask); System.out.println(Thread.currentThread().getName()); t.start(); } } class Ticketsimplements Callable { //重写call⽅法 @Override public Object call() throws Exception { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接⼝通过FutureTask包装器来实现的线程" return null; } }
1. ⾼并发、任务执⾏时间短的业务:线程池线程数可以设置为 CPU 核数 +1 ,减少线程上下⽂的切换。2. 并发不⾼、任务执⾏时间⻓的业务要区分开看:a. 假如是业务时间⻓集中在 IO 操作上,也就是 IO 密集型的任务,因为 IO 操作并不占⽤ CPU ,所以不要让所有的 CPU闲下来,可以加⼤线程池中的线程数⽬,让CPU 处理更多的业务b. 假如是业务时间⻓集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)⼀样吧,线程池中的线程数设置得少⼀些,减少线程上下⽂的切换3. 并发⾼、业务执⾏时间⻓,解决这种类型任务的关键不在于线程池⽽在于整体架构的设计,看看这些业务⾥⾯某些数据是否能做缓存是第⼀步,增加服务器是第⼆步,⾄于线程池的设置,设置参考(2)。最后,业务执⾏时间⻓的问题,也可能需要分析⼀下,看看能不能使⽤中间件对任务进⾏拆分和解耦。
1、如果你使⽤的 LinkedBlockingQueue ,也就是⽆界队列的话,没关系,继续添加任务到阻塞队列中等待执⾏,因为LinkedBlockingQueue可以近乎认为是⼀个⽆穷⼤的队列,可以⽆限存放任务;2 、如果你使⽤的是有界队列⽐⽅说 ArrayBlockingQueue 的话,任务⾸先会被添加到 ArrayBlockingQueue 中,ArrayBlockingQueue满了,则会使⽤拒绝策略 RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy。
1. ⽅法锁( synchronized 修饰⽅法时)a. 通过在⽅法声明中加⼊ synchronized 关键字来声明 synchronized ⽅法。b. synchronized ⽅法控制对类成员变量的访问:c. 每个类实例对应⼀把锁,每个 synchronized ⽅法都必须获得调⽤该⽅法的类实例的锁⽅能执⾏,否则所属线程阻塞,⽅法⼀旦执⾏,就独占该锁 ,直到从该⽅法返回时才将锁释放,此后被阻塞的线程⽅能获得该锁,重新进⼊可 执⾏状态。这种机制确保了同⼀时刻对于每⼀个类实例,其所有声明为 synchronized 的成员函数中⾄多只有⼀个处于可执⾏状态,从⽽有效避免了类成员变量的访问冲突。2. 对象锁( synchronized 修饰⽅法或代码块)a. 当⼀个对象中有 synchronized method 或 synchronized block 的时候调⽤此对象的同步⽅法或进⼊其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调⽤者占⽤,则需要等待此锁被释放。(⽅法锁也是对象锁)b. java 的所有对象都含有 1 个互斥锁,这个锁由 JVM ⾃动获取和释放。线程进⼊ synchronized ⽅法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized ⽅法正常返回或者抛异常⽽终⽌,JVM 会⾃动释放对象锁 。这⾥也体现了⽤ synchronized 来加锁的 1 个好处,⽅法抛异常的时候,锁仍然可以由JVM来⾃动释放。3. 类锁( synchronized 修饰静态的⽅法或代码块)a. 由于⼀个 class 不论被实例化多少次,其中的静态⽅法和静态变量在内存中都只有⼀份。所以,⼀旦⼀个静态的⽅法被申明为synchronized 。此类所有的实例化对象在 调⽤此⽅法 ,共⽤同⼀把锁,我们称之为类锁。4. 对象锁是⽤来控制实例⽅法之间的同步,类锁是⽤来控制静态⽅法(或静态变量互斥体)之间的同步。
synchronized⽅法正常返回或者抛异常⽽终⽌,JVM会⾃动释放对象锁
1. 解释⼀:并⾏是指两个或者多个事件在同⼀时刻发⽣;⽽并发是指两个或多个事件在同⼀时间间隔发⽣。2. 解释⼆:并⾏是在不同实体上的多个事件,并发是在同⼀实体上的多个事件。3. 解释三:在⼀台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如 hadoop 分布式集群。所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。
1. volatile 只能保证你数据的可⻅性,获取到的是最新的数据,不能保证原⼦性;2. ⽤ AtomicInteger 保证原⼦性。3. synchronized 既能保证共享变量可⻅性,也可以保证锁内操作的原⼦性。
1、如果这个异常没有被捕获的话,这个线程就停⽌执⾏了。2.、 另外重要的⼀点是:如果这个线程持有某个对象的监视器,那么这个对象监视器会被⽴即释放。
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进⾏唤起和等待,⽐⽅说阻塞队列BlockingQueue就是为线程之间共享数据⽽设计的。
- public class Ticket implements Runnable {
- private int ticket = 10;
-
- public void run() {
- while (ticket > 0) {
- ticket--;
- System.out.println("当前票数为:" + ticket);
- }
- }
- }
-
- package 多线程共享数据;
-
- public class SellTicket {
- public static void main(String[] args) {
- Ticket t = new Ticket();
- new Thread(t).start();
- new Thread(t).start();
- }
- }
2、银行存取款
- public class MyData {
- private int j = 0;
-
- public synchronized void add() {
- j++;
- System.out.println("线程" + Thread.currentThread().getName() + "j为:" + j);
- }
-
- public synchronized void dec() {
- j--;
- System.out.println("线程" + Thread.currentThread().getName() + "j为:" + j);
- }
-
- public int getData() {
- return j;
- }
- }
-
- public class AddRunnable implements Runnable {
- MyData data;
-
- public AddRunnable(MyData data) {
- this.data = data;
- }
-
- public void run() {
- data.add();
- }
- }
-
- public class DecRunnable implements Runnable {
- MyData data;
-
- public DecRunnable(MyData data) {
- this.data = data;
- }
-
- public void run() {
- data.dec();
- }
- }
-
- public class TestOne {
- public static void main(String[] args) {
- MyData data = new MyData();
- Runnable add = new AddRunnable(data);
- Runnable dec = new DecRunnable(data);
- for (int i = 0; i < 2; i++) {
- new Thread(add).start();
- new Thread(dec).start();
- }
- }
- }