目录
1、继承Thread类,重写run()方法 特点:java单继承,不灵活,不推荐使用。
2、实现Runable接口,重写run()方法;
3、实现Callable接口,重写call()方法;
4、线程池创建线程
1、创建(new)状态 执行了new Thread(); 创建完成后就需要为线程分配内存;
2、就绪(runnable)状态: 调用了start()方法, 等待CPU进行调度
3、运行(running)状态: 执行run()方法
4、阻塞(blocked)状态: 暂时停止执行线程,将线程挂起( sleep()、wait()、join(),
没有获取到锁都会使线程阻塞), 可能将资源交给其它线程使用。
5、死亡(terminated)状态: 线程销毁(正常执行完毕、发生异常
或者被打断interrupt()都会导致线程终止)

注:
wait(): 导致线程进入等待阻塞状态,仅会释放当前锁,
会一直等待直到它被其他线程通过notify()或者notifyAll()唤醒。
notify(): 该方法只能在同步方法或同步块内部调用, 随机选择一个;
notifyAll(): 唤醒所有的wait()对象。
start()方法用于启动一个线程,run()用于执行线程的运行时代码。
run()可以重复调用,start()而只能调用一次
yield() 此线程会让出cup的占用权利,由运行状态变为就绪状态;
sleep()此线程会进入睡眠状态,不会释放锁;
wait() 此线程不会自动苏醒,释放锁,配合notify()或者notifyAll()使用;
interrupt()用于中断线程; isInterrupt()查看当前中断信号是true还是false;
1、类型不同:sleep()是Thread线程类的静态返方法, wait()是Object类的方法;
2、是否释放锁:sleep()不释放锁, wait()释放锁;
3、用途不同:sleep()通常被用于暂停执行, wait()通常被用于线程交互/通信;
4、用法不同:sleep()执行完成后,线程会自动苏醒;wait()被调用后,线程不会自动苏醒,需要配合notify()或者notifyAll()使用。
【注:wait(long timeout)超时后线程会自动苏醒】
为了减少每次获取资源的消耗,提高对资源的利用率。
使用线程池的好处:1、降低资源消耗
2、提高响应速度
3、提高线程的可管理性
线程池参数:
corePoolsize 核心线程数 也就是正常情况下创建工作的线程数,这些线程创建后并不会消除,而是一种常驻线程。
maxinumPoolsize 最大线程数 当任务比较多时,核心线程数用完时,还无法满足时,此时创建新的线程,但线程内的线程数不会超过最大线程数。
workQueue 用来存放执行任务的
ThreadFactory 实际上是一个线程工厂,用来生产线程执行任务的。
1、newCachedThreadPool创建一个可缓存线程池
2、newFixedThreadPool 创建一个可重用的固定线程数的线程池
3、newScheduledThreadPool 创建一个定长线程池
4、newSingleThreadExecutor 创建一个单线程化的线程池
当线程A持有独占锁a,并尝试获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
(比如两人去吃西餐,吃西餐需要刀、叉;此时只有一对刀叉,A拿了刀,B拿了叉,两人要想吃到西餐,需要获取对方手中的刀或叉。)
1、尽量使用tryLock(long timeout,TimeUnit unit)的方法,设置过期时间,超时可以退出
2、尽量使用java.util.concurrent并发类代替自己手写锁
3、尽量减少同步代码块;
4、尽量几个功能不要使用同一把锁
1、volatile是变量修饰符;synchronized可以给方法、代码块加锁;
2、volatile可以保证变量的修改可见性;synchronized可以保证变量的修改可见性和原子性;
3、volatile不会造成线程阻塞;synchronized可能造成线程阻塞;
1、synchronized可以给方法、代码块加锁,而lock只能给代码块加锁;
2、synchronized不需要手动获取和释放锁,lock需要自己加锁和释放锁;
ReentrantLock: 实现了Lock接口 是可重入锁,底层是AQS(AbstractQueuedSynchronized)实现的,支持公平锁和非公平锁。
公平锁:先判断AQS队列是否需要排队;
非公平锁:不判断直接竞争锁;
synchronized默认采用的是偏向锁,在程序运行中始终只有一个线程获取锁,java对象中记录这个线程id,所以在下次获取synchronized锁时,只需要比较线程id。
在运行过程中,如果出现第二个线程请求synchronized锁时,分两种情况:
第一种:在没有并发竞争锁的情况下,synchronized锁会自动升级为轻量级锁,这个时候第二个线程就会尝试自旋锁的方法获取锁,因为很快就能够拿到锁,所以线程不会阻塞。
第二种:在两个线程竞争锁的情况下,synchronized锁会升级为重量级锁,此时只有一个线程获取锁,另一个线程会被阻塞,只有等线程释放锁,另一个线程才能获取锁。