- JVM 同步基于进入和退出,使用 管程 对象 (monitor 监视器)实现
- 进程 1.
- 线程
- 进程里的最小执行单元
- 用户线程:
new Thread()
- 守护线程:jvm垃圾回收
- 多线程:共享资源,资源争夺
- 创建线程
- 继承 Thread.class
- 实现 Runnable
- 使用 Callable -call()
- 使用线程池
// 当前正在执行的线程 睡眠
Thread.sleep();
// 当前正在执行的线程 谦让的退出 ---> 回到等待队列
Thread.yield();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread t2 = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
if (i == 3) {
// t2 线程正在执行中 ,t1 join 优先加入执行完毕后,t2 才可以继续执行
t1.join();
}
System.out.println("t2=======>"+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
}
// 如果在 主线程中调用 ,可以保证 线程执行完毕后, 有序结束
t1.join();
t2.join();
System.out.println(" main .....end ");
synchronized
- 既保证了原子性 又保证了可见性
- 可重入锁
public synchronized static void testLock() {
// synchronized == synchronized (Ticket.class){}
}
public synchronized void outket() {
// public synchronized void outket() === synchronized (this){}
}
/* 锁 this 独占 */
public class Ticket {
Integer money;
public synchronized void add() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.money = 100;
}
public synchronized Integer get() {
return this.money;
}
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t::add).start();
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.get());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.get());
}
}
// 可重入锁
synchronized void a(){
b();
}
synchronized void b(){}
- 程序在执行过程中,如果出现异常,默认情况锁会被释放
- 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。
- 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,
- 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。
- 因此要非常小心的处理同步业务逻辑中的异常
synchronized的底层实现
JDK早期的 重量级 - OS
后来的改进
锁升级的概念:
sync (Object)
markword 记录这个线程ID (偏向锁)
如果线程争用:升级为 自旋锁
10次以后,
升级为重量级锁 - OS
执行时间短(加锁代码),线程数少,用自旋
执行时间长,线程数多,用系统锁
适用
- 执行时间短 (自旋锁 等待消耗资源)
- 线程数量少 (1个线程上锁, 9999999 自旋等待)
lock
- 手动上锁,解锁
lock unlock
- 读写锁
- 可重入锁