线程的状态【理解】
NEW(新建状态): 至今尚未启动的线程处于这种状态。
RUNNABLE(就绪状态):正在 Java 虚拟机中执行的线程处于这种状态。
BLOCKED(阻塞状态): 受阻塞并等待某个监视器锁的线程处于这种状态。
WAITING(等待状态): 无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
TIMED_WAITING(计时等待): 等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
TERMINATED(销毁状态):已退出的线程处于这种状态。
线程池【掌握】
频繁的创建和销毁线程比较浪费系统资源,同时比降低系统的性能。Java给我们提供了线程池技术,你可以把线程池理解成是一个容器,这容器中存储了若干个线程,当需要线程执行任务时就从池子中取线程,用完了之后就还回去。
获取线程池并使用
在Executors类中提供了获取线程池的方法,可以获取不同特点的线程池。
public static ExecutorService newCachedThreadPool()
可以创建新线程的线程池
public static ExecutorService newFixedThreadPool(int nThreads)
获取最大允许有指定线程数量的线程池。
//创建一个可以根据需要创建新线程的线程池
ExecutorService es = Executors.newCachedThreadPool();
//创建一个最大线程数量的线程池
//ExecutorService es = Executors.newFixedThreadPool(10);
//提交11个线程任务
for (int i = 0; i < 11; i++) {
es.submit(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+"线程执行了");
}
});
}
//关闭线程池
es.shutdown();
ThreadPoolExceutor创建线程池
//创建线程池的构造方法
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //空闲线程最大的存活时间
TimeUnit unit, //时间的单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //创建线程的工厂
RejectedExecutionHandler handler)
//拒绝策略(任务数量>最大线程数+队列的容量)
- 拒绝策略
static class ThreadPoolExecutor.AbortPolicy
用于被拒绝任务的处理程序,它将抛出 RejectedExecutionException.
static class ThreadPoolExecutor.CallerRunsPolicy
用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。
static class ThreadPoolExecutor.DiscardOldestPolicy
用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试 execute;如果执行程序已关闭,则会丢弃该任务。
static class ThreadPoolExecutor.DiscardPolicy
用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
6, //核心线程数6个
9, //最大线程数9个
10, //空闲线程存活时间10秒
TimeUnit.SECONDS, //时间单位,秒
new ArrayBlockingQueue<>(10), //阻塞队列
Executors.defaultThreadFactory(), //创建线程的工厂
new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);
for (int i = 0; i < 20; i++) {
poolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName()+"线程执行了");
});
}
//关闭线程池
poolExecutor.shutdown();
JVM的内存模型【了解】
volatile关键字【了解】
被volatile修饰的变量,当这个变量的值被线程修改后,其他线程可以及时的发现最新值。
public class MyNumber {
public static volatile int num=100;
}
public class MyThread1 extends Thread{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyNumber.num=90;
System.out.println("线程1修改了num的为90");
}
}
public class MyThread2 extends Thread{
@Override
public void run() {
while (true){
if(MyNumber.num!=100){
System.out.println("线程2发现数据被修改了");
break;
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
MyThread1 t1=new MyThread1();
MyThread2 t2=new MyThread2();
t1.start();
t2.start();
}
}
原子性问题【了解】
原子性指的是,多个操作要么同时成功,要么同时失败。Java中的自增运算是非原子性的
自增元素的底层原理:
1.在线程的本地内存拷贝一个变量的副本
2.再对变量副本进行自增
3.把变量副本的值写回主内存
以上的3个步骤是非原子性的,意思就是说这个三个步骤的任何一步都可能被其他线程打断。
这样就打破了原子性多个步骤同时成功的要求。
多线程在操作共享数据时,是非原子性。为了解决这个问题Java的API提供了一些原子类,如AtomicInteger的使用如下。
- 构造方法
AtomicInteger()
创建具有初始值 0 的新 AtomicInteger。
AtomicInteger(int initialValue)
创建具有给定初始值的新 AtomicInteger。
- 常用方法
public int addAndGet(int delta)
以原子方式将给定值与当前值相加。 //类似于 a+=2;
public int incrementAndGet()
先自增,再获取 //类似于 ++a;
public int getAndIncrement()
先获取,再自增 //类似于 a++
public int getAndSet(int newValue)
先获取旧值,再修改成新值 // 类似于 int x=3; y=x; x=50;
AtomicInteger的底层原理如图
HashMap和Hashtable【了解】
共同点:HashMap和Hashtable都是双列集合,底层的数据结构都是哈希表结构
不同点:HashMap线程不安全的
Hashtable是线程安全的,底层对每一个方法都加上了synchronized关键字。
扩展
ArrayList: 线程不安全的
Vector: 线程安全的
StringBuffer:线程安全的
StringBuilder: 线程不安全的
ConcurrentHashMap【了解】
ConcurrentHashMap和HashMap的用法是一样的,但是ConcurrentHashMap是线程安全的,同时相对于Hashtable效率要高一些。
- JDK1.7的底层原理
ConcurrentHashMap底层:数组+哈希表(数组+链表)
当添加元素时:添加元素时使用 synchronized 把数组中的小哈希表锁住
- JDK1.8的原理
ConcurrentHashMap底层:数组+哈希表(数组+链表)
当添加元素时:采用CAS算法 + synchronized 解决安全问题
CountDownLatch 线程计数器【了解】
1.使用场景: 可以让一个线程在其他线程执行完毕后再执行
2.使用步骤:
//创建线程计数器,为3个
CountDownLatch cdl=new CountDownLatch(3);
//线程1 cdl.countDown(); //底层会让计数器-1
//线程2 cdl.countDown(); //底层会让计数器-1
//线程3 cdl.countDown(); //底层会让计数器-1
//等以上线程执行完毕之后,我想要的线程才执行
//想要执行的线程 cdl.await(); 前面没有执行完,它就等待,如果前面几个线程执行完了,就会继续执行。
SemaPhore
SemaPhore主要用来控制线程的访问数量
public class MyRunnable implements Runnable{
//创建SemaPhore对象,指定管理线程的数量2
SemaPhore sp=new SemaPhore(2);
//复写run方法
@Override
public void run(){
for(int i=0;i<100;i++){
//获取通行证 void acquire()
sp.acquire();
System.out.println("汽车正在行驶");
try{
Thread.sleep(100);
}catch(Exeption e){
e.printStrackTrac();
}
//释放通行证 void release()
sp.release();
}
}
}