网站建设经典范例,网站英文域名是什么,wordpress year,中国纪检监察报评论员文章多线程 了解多线程
多线程是指从软件或者硬件上实现多个线程并发执行的技术。
具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程#xff0c;提升性能。
并发和并行
并行#xff1a;在同一时刻#xff0c;有多个指令在CPU上同时执行并发#xff1…多线程 了解多线程
多线程是指从软件或者硬件上实现多个线程并发执行的技术。
具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程提升性能。
并发和并行
并行在同一时刻有多个指令在CPU上同时执行并发在同一时刻有多个指令在CPU上交替执行
进程和线程
进程正在运行的软件
独立性进程是一个独立运行的基本单位同时也是系统分配调度资源的独立单位。动态性进程的实质就是程序的一次执行过程动态产生动态消亡。并发性任何进程都可以和其他进程并发执行
线程是进程中单个顺序控制流是一条执行路径
单线程一个进程如果只有一条执行路径则称为单线程程序多线程一个进程如果有多条执行路径则成为多线程程序
多进程的实现方案
继承Thread类的方式进行实现 定义一个类继承Thread类在定义的类中重写run()方法创建自定义类的对象启动线程 实现Runable接口的方式实现 自定义一个类实现Runable接口在自定义类中重写run方法创建自定义对象创建Tread类对象把自定义对象作为构造方法的参数启动线程 利用Callable和Future接口方式实现 定义一个类实现Callable接口在该类中重写call方法创建自定义类的对象创建Future的实现类FutureTask对象把自定义类对象作为构造方法的参数创建Tread类的对象把FutureTask对象最为构造方法的参数启动线程
三种方式的对比
优点缺点实现RunnableCallable接口扩展性强实现该接口的同时还可以继承其他类编程相对复杂不能直接使用Thread类中的方法继承Thread类编程比较简单可以直接使用Thread类中的方法可扩展性差不能继承其他类
使用getName获取当前正在执行现成的名称
使用setName()给当前线程设置名称也可使用子类的带参构造方法设置名称
使用Thread的Sleep方法 使得线程睡眠特定的时间
实现Runnable结构创建多线程程序的好处
避免了单继承的局限性 一个类只能继承一个类自定义类继承了Thread类就不能继承其他类实现了Runnable接口还可以继承其他类实现其它接口 增强了程序的扩展性降低了程序的耦合性 实现了Runnable接口的方式把设置线程任务和开启新线程进行了分离解耦实现类中重写了run方法用来设置线程任务创建Thread类对象调用start方法用来开启新的线程
线程安全问题
解决措施
使用同步机制解决
方法一同步代码块
syncheonized(对象){代码块
}方法二同步方法
使用步骤
把访问了共享数据的代码提取出来放到一个方法中在方法上添加synchronized修饰符格式
修饰符 synchronized 返回值类型 方法名参数列表{可能会出现安全问题的代码访问了共享数据的代码
}
线程池
线程池可以看成是一个池子这个池子中存储很多个线程
系统创建一个线程的成本是很高的因为他涉及到与操作系统的交互当程序需要创建大量生存期很短暂的线程是频繁的创建何晓辉线程对系统的资源消耗可能大于业务处理对线程的消耗。为了提高性能我们可以采用线程池。
线程池在启动时会创建大量空闲线程当我们向线程池提价搜任务是线程池就会启动一个线程来执行该任务。等待任务执行完毕线程并不会死亡而是咋次返回到线程池中称为空闲状态等待哦下一次任务的执行。
线程池的设计思路
准备一个任务容器一次性启动多个消费者线程刚开始任务容器是空的所有线程都在等待知道一个外部线程向这个任务容器扔了一个“任务”就会有一个消费者线程被唤醒这个消费者线程取出任务并执行任务执行完毕后继续等待下一次任务的到来
线程池-Executors默认线程池
概述在开发中我们使用JDK中自带的线程池
我们可以使用Excutors中所提供的的静态方法来创建线程池
static ExcutorsService newCachedThreadPool()创建一个默认线程池
static newFixedThreadPool(int nThreads)创建一个指定最多线程数量的线程池
代码实现
ackage practise2.Exam2;import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//static ExecutorsService new CachedThreadPool() 创建一个默认的线程池
//static newFixedThreadsPool(int nThreads) 创建一个指定最多线程数量的线程池
public class Exam1 {public static void main(String[] args) {//创建一个默认线程池对象池子是空的默认最多可以容纳int类型的最大值ExecutorService executorService Executors.newCachedThreadPool();//Executors ---可以帮助我们创建线程池对象//ExecutorService ---可以帮助我们控制线程池executorService.submit(()-{System.out.println(Thread.currentThread().getName()在执行了);});executorService.submit(()-{System.out.println(Thread.currentThread().getName()在执行了);});executorService.shutdown();}
}
线程池-Executors创建指定上限的线程池
使用Executors中所提供的静态方法来创建线程池
static ExecutorsService newFixcedThreadPool(int nThread):创建一个指定最多线程数量的线程池
代码实现
package practise2.Exam2;import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;//static ExecutorService newFixedThreadPool(int nThread):创建一个指定最多线程数量的线程池
public class Exam2 {public static void main(String[] args) {//参数不是初始值而是最大值ExecutorService executorService Executors.newFixedThreadPool(10);ThreadPoolExecutor pool (ThreadPoolExecutor) executorService;System.out.println(pool.getPoolSize()); //0executorService.submit(()-{System.out.println(Thread.currentThread().getName()在执行了);});executorService.submit(()-{System.out.println(Thread.currentThread().getName()在执行了);});System.out.println(pool.getPoolSize());//2//executorService.shutdown();}
}线程池-ThreadPoolExecutor
创建线程池对象
ThreadPoolExecutors threadPoolExecutor new ThreadPoolExecutor(核心线程数连发最大线程数量空闲线程数量空闲线程最大存活时间任务队列创建线程工厂任务的拒绝策略)
代码实现
package practise2.Exam2;import pracise1.exam5.MyRunnable;import java.util.concurrent.*;public class Exam3 {public static void main(String[] args) {//参数一核心线程数量//参数二最大线程数//参数三空闲线程最大存活时间//参数四时间单位//参数五任务队列//参数六创建线程工厂//参数七人物的拒绝策略ThreadPoolExecutor pool new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());pool.submit(new MyRunnable());pool.submit(new MyRunnable());pool.shutdown();}
}线程池-参数详解
创建线程池对象
ThreadPoolExecutor threadPoolExecutor new ThreadPoolExecutor(
核心线程数量最大线程数量空闲线程最大存活时间任务队列创建线程工厂任务的拒绝策略
)
参数含义限制参数一核心线程数量不能小于0参数二最大线程数不能小于等于0最大线程数大于等于核心线程数参数三空闲线程最大存活空间不能小于0参数四时间单位时间单位参数五任务队列不能为null参数六创建线程工厂不能为null参数七人物的拒绝策略不能为null
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
corePoolSize 核心线程的最大值不能小于0
maximumPoolSize最大线程数不能小于等于0maximumPoolSize corePoolSize
keepAliveTime 空闲线程最大存活时间,不能小于0
unit 时间单位
workQueue 任务队列不能为null
threadFactory 创建线程工厂,不能为null
handler 任务的拒绝策略,不能为null
线程池-非默认任务拒绝策略
RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口他下面存在4个子类。
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor.DiscardPolicy: 丢弃任务但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor。CallerRunsPolicy 调佣任务的run方法绕过线程池直接执行注明确线程池对多可执行的任务数队列容量最大线程数
package practise2.Exam2;import java.util.concurrent.*;public class Exam {public static void main(String[] args) {
// ThreadPoolExecutor threadPoolExecutornew ThreadPoolExecutor(1,3,20,
// TimeUnit.SECONDS,new ArrayBlockingQueue(1),
// Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//提交5个任务而该线程池最多可以处理4个任务当我们使用ABortPOlicy这个任务处理策略是后就会抛出异常ThreadPoolExecutor threadPoolExecutornew ThreadPoolExecutor(1,3,20,TimeUnit.SECONDS,new ArrayBlockingQueue(1),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());//提交了5个任务for (int i 0; i 5; i) {//定义一个变量来制定当前执行的任务这个变量需要被final修饰final int yi;threadPoolExecutor.submit(()-{//System.out.println(Thread.currentThread().getName()------执行了任务);System.out.println(Thread.currentThread().getName()----执行了任务y);});}}
}
package practise2.Exam2;import java.util.concurrent.*;public class Exam5 {public static void main(String[] args) {ThreadPoolExecutor threadPoolExecutornew ThreadPoolExecutor(1,3,20, TimeUnit.SECONDS,new ArrayBlockingQueue(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());//提交五个任务for (int i 0; i 5; i) {threadPoolExecutor.submit(()-{System.out.println(Thread.currentThread().getName()----执行了任务);});}}
}
通过控制台的输出我们可以看次策略没有通过线程池中得到线程执行任务而是直接调用任务的run()方法绕过线程池直接执行。
原子性
volatile-问题
代码分析 volatile解决
以上案例出现的问题
当A线程修改了共享数据时B线程没有及时获取道最新的值如果还在使用原先的值就会出现问题。
堆内存是唯一的每一个线程都有自己的线程栈每一个线程在使用堆内存里面的变量时都会先拷贝一份到变量的副本中在线程中每一次使用是从变量的副本中获取的
volatile关键字强制线程在每一次使用时都会看一下公共区域最新的值。
public class Money {public static volatile int money100000;
}
public class MyThread1 extends Thread{Overridepublic void run() {while (Money.money100000){}System.out.println(结婚基金已经不是十万了);}
}
public class MyThread2 extends Thread{Overridepublic void run() {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}Money.money90000;}
}
public class Demo {public static void main(String[] args) {MyThread1 myThread1new MyThread1();myThread1.setName(小垃圾);myThread1.start();MyThread2 myThread2new MyThread2();myThread2.setName(小趴菜);myThread2.start();}}
synchronized解决
线程获得锁清空变量副本拷贝共享最新的值到变量副本中执行代码将修改后变量副本中的值赋值给共享数据释放锁
代码实现 原子性
概述:在一次操作或者多次操作中要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断要么所有的操作都不执行多个操作是一个不可分割的整体。
Volatile关键字不能保证原子性
解决方案我们可以给count操作添加锁那么count操作就是临界区中的代码临界区中的代码一次只能被一个线程去执行所以count就变成了的原子操作。 原子性—AtomicInteger
概述java从JDK1.5开始提供了java.uyil.concurrent.atomic包简称Atomic包这个包中的原子提供了一种用法简单性能高效线程安全地根新一个变量的方式因为变量的类型有很多个所以在Atomic包中一共提供了13个类属于4种类型的原子更新方式分别是
原子更新基本类型原子更新数组原子更新引用和原子更行属性字段
使用原子的方式甘心基本类型使用原子的方式更新基本类型Atomic包提供了一下3个类
AtomicBoolean:原子更新布尔类型
AtomicInteger:原子更新整型
AtomicLong:原子更新长整型
以上三个类提供的方法几乎一模一样以AtomicInteger为例讲解
public AtomicInteger(); //初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): //初始化一个指定值的原子型Integerint get(); //获取值
int getAndIncrement(); //以原子的方式将当前值加1注意这里返回的是自增前的值
int incrementAndGet(); //以原子的方式将当前值加1注意这里返回的是自增前的值
int addAndGet(int data): //以原子的方式将输入的数值与示例中的值AtomicInteger里的value)相加并返回结果。
int getandSet(int value): //以原子方式设置为newValur的值并返回旧值代码实现 AtomicInteger-内存解析
AtomicInteger原理
自旋锁CAS算法
CAS算法
有三个操作数内存值V旧的预期值A要修改的值B)
当旧的预期值A内存值 此时修改成功将V改为B
当旧的预期值A内存值此时修改失败不做任何操作
并重新获取现在的最新值这个重新获取的动作就是自旋
AtomicInteger-源码解析
代码实现: 源码解析 乐观锁和悲观锁
synchronized和CAS的区别
相同点
在多线程的情况下都可以保证共享数据的安全性
不同点
synchronized总是从最坏的角度出发认为每次获取数据的时候别人都有可能修改。所以在每次操作共享数据之前都会上锁。悲观锁
cas是从乐观的角度出发假设每次获取数据别人都不会修改所以不会上锁只不过在修改共享数据的时候会检查一下。
如果别人修改过则获取最新数据。
如果别人没有修改过那么我们直接修改共享数据的值乐观锁
并发工具类
并发 工具类-Hashtable
Hashtable出现的原因
在集合类中HashMap是比较常用的集合类对象但是HashMap是现成不安全的多线程环境下可能会存在问题。为了保证数据得到安全性我们可以使用Hashtable但是Hashtable的效率低下。
代码实现 并发工具类-ConcurrentHashMap基本使用
ConcurrentHashMap出现的原因
在集合类中HashMap是比较常用的集合对象但是hashMap是线程不安全的多线程环境下可能会存在的问题。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下基于以上两个原因我们可以使用JDK1.5以后提供的ConcurrentHashMap。
体系结构
ConcurrentHashMap
Map接口
HashMapHashtableTreeMapConcurrentMap
总结
HashMap是线程不安全的多线程环境下有数据安全问题hashtable 是线程安全的但是会将整张表锁起来效率低下‘ConcurrentHashMap也是线程安全的效率高在JDK7和JDK8中底层原理不同