1、获取的锁的分类:获取对象锁和获取类锁
获取对象锁的两种方法:一是同步代码块(synchronized(this),sybchronized(类实例对象)),锁是小括号()中的实例对象;二是同步非静态方法(synchronized method),锁是当前对象的实例对象。
package com.javabasic.bytecode.thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")){
async();
} else if (threadName.startsWith("B")){
syncObjectBlock1();
} else if (threadName.startsWith("C")){
syncObjectMethod1();
}
}
/*synchronized修饰非静态方法*/
private synchronized void syncObjectMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e){
e.printStackTrace();
}
}
/*方法中有synchronized(this|objrct){}同步代码块*/
private void syncObjectBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this){
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
/*异步方法*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_Async_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread A_thread1 = new Thread(syncThread,"A_thread1");
Thread A_thread2 = new Thread(syncThread,"A_thread2");
Thread B_thread1 = new Thread(syncThread,"B_thread1");
Thread B_thread2 = new Thread(syncThread,"B_thread2");
Thread C_thread1 = new Thread(syncThread,"C_thread1");
Thread C_thread2 = new Thread(syncThread,"C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();;
C_thread2.start();
}
}
获取类锁的两种方法:一是同步代码块(synchronized(类.class)),锁是小括号()中的类对象(Class对象);二是同步静态方法(synchronized static method),锁是当前对象的类对象(Class对象)。
package com.javabasic.bytecode.thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")){
async();
} else if (threadName.startsWith("D")){
syncClassBlock1();
} else if (threadName.startsWith("E")){
syncClassMethod1();
}
}
/*异步方法*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_Async_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e){
e.printStackTrace();
}
}
private void syncClassBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class){
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
private synchronized static void syncClassMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread A_thread1 = new Thread(new SyncThread(),"A_thread1");
Thread A_thread2 = new Thread(new SyncThread(),"A_thread2");
Thread D_thread1 = new Thread(new SyncThread(),"D_thread1");
Thread D_thread2 = new Thread(new SyncThread(),"D_thread2");
Thread E_thread1 = new Thread(new SyncThread(),"E_thread1");
Thread E_thread2 = new Thread(new SyncThread(),"E_thread2");
A_thread1.start();
A_thread2.start();
D_thread1.start();
D_thread2.start();
E_thread1.start();
E_thread2.start();
}
}
对象锁和类锁的总结:使用对象锁时,多个线程访问同一个对象,会产生竞争关系,但是多个线程访问同一个类实例化的不同对象,则不会发生竞争关系,即多个线程之间互不干扰;使用类锁时,多个线程访问同一对象或者是同一类实例化的不同对象时,都会发生竞争关系;同一个类中的类锁和对象锁不会相互干扰,不会有竞争关系。
2、自旋锁和自适应锁
自旋锁:
- 在许多情况下,共享数据的锁定状态的持续时间较短,此时切换线程不值得
- 在线程阻塞的时候,让线程执行忙循环等待锁的释放,不让出CPU
- 缺点:若等待的锁被其他线程长时间占用,会带来许多性能上的开销
自适应自旋锁:
- 自选的次数不再固定
- 由上一次在同一个锁上的自旋时间和锁的拥有着的状态来决定
3、锁消除和锁粗化
锁消除是JVM更彻底的优化,JIT编译时,对上下文进行扫描,去除不可能存在竞争的锁;锁粗化时另一种极端,通过加锁的范围,避免反复加锁和解锁
4、synchronized的四种状态
无锁、偏向锁、轻量级锁、重量级锁
偏向锁:较少同一线程获取锁的代价。大多数情况下,所不存在多线程竞争关系,总是由同一线程多次获得。
轻量级锁:是由偏向锁升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁。适用于线程交替执行的同步块。若存在同一时间两个线程访问同一锁的情况,就会导致轻量级锁升级为重量级锁
5、synchronized和ReentrantLock(再入锁)
ReentrantLock(再入锁):能够实现比synchronized更细粒度的控制,如控制fairness(锁的公平);调用lock()之后,必须调用unlock()来释放锁;性能未必比synchronized高,并且也是可重入的。
ReentrantLock公平性的设置 :ReentrantLock fairLock = new ReentrantLock(true);参数为true时,倾向与将锁赋予等待时间最久的线程;公平锁即获取锁的顺序按先后调用lock方法的顺序(慎用);非公平锁即抢占的顺序不一定,看运气;synchronized是非公平锁。
ReentrantLock将锁对象化:判断是否有线程,或者某个特定的线程在排队等待获取锁;带超时的获取锁的尝试;能感知有没有成功获取锁。
总结:synchronzied是关键字,ReentrantLock是类;ReentrantLock可以对对获取锁的时间进行设置,避免线程死锁;ReentrantLock可以获取各种锁的信息;ReentrantLock可以灵活的实现多路通知;锁的机制不同:sync操作的是Mark Word,ReentrantLock调用的是Unsafe类的park()方法。
6、Java线程池
为什么要使用线程池:降低资源消耗,提高线程的可管理性。
7、实现线程池——ThreadPoolExecutor类
ThreadPoolExecutorThreadPoolExecutor的构造函数:
- corePoolSize:核心线程数量
- maximumPoolSize:线程不够用时能够创建的最大线程数
- workQueue:任务等待队列
- keepAliveTime:线程池维护线程所允许的空闲时间,当线程池中的线程数量大于corePoolSize的时候,如果这是没有新的任务提交,核心线程外的线程不会立即被销毁而是会等待,知道等待的时间超过keepAliveTime才会被销毁
- threadFactory:创建新线程
- hander:线程池的饱和策略。如果阻塞队列满了,并且没有空闲的线程,这时如果继续提交任务,会通过hander所指定的策略来处理线程。
线程池的状态:
- RUNNING:能够接受新提交的任务,并且也能处理阻塞队列中的任务
- SHUTDOWN:不在接受新提交的任务,但可以处理存量任务
- STOP:不在接受新提交的任务,也不处理存量任务
- TIDYING:所有任务都已经终止
- TERMINATED:terminated()方法执行完便会进入此状态(一般情况下terminated方法中什么操作都没有,只是线程池结束的一个标志)