每日一狗(田园犬西瓜瓜)

用于实现同步处理保障数据共享的数据安全性。
数据安全问题是由 共享数据 和 共享数据的修改所导致的。
充当锁对象的对象数量要比线程数量要少,要不就达不到锁的效果,线程在执行到这个代码块的时候,需要获取这个锁对象才能去执行这个代码块。
注意:锁对象最小要比线程对象才能有锁的效果,但是要保证数据的一致性还是锁对象还是要唯一,即同一时刻内,只能有一个线程操作共享数据,其他线程无法对其进行操作。
public synchronized void add(){
// 会被串行的代码块
}
private synchronized static void add() {
// 会被串行的代码块
}
synchronized(锁对象){
// 会被串行的代码块
}
建议使用同步代码块的上锁,颗粒度较小,可以尽可能的提升程序的并发性,提高性能。
package com.yang1;
public class Test01 {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new MyThread("" + (i + 1)).start();
}
}
}
class MyThread extends Thread {
private String name;
private static int count;
private static String suo = "suo";
public MyThread(String name) {
this.name = name;
count = 20;
}
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (suo) {
if (count > 0) {
System.out.println(name + "窗口迈出了底" + count + "张票");
count--;
} else {
System.out.println("票以买完");
}
}
}
}
}
当前对象中的synchronized修饰的所有的方法互斥,
不能添加到构造方法中,
拿到锁的线程可以随意执行被锁修饰的所有的方法,但是其他线程就要等待那锁的线程释放锁后去竞争到锁后才能调用被锁修饰的所有方法。
线程重入:避免了死锁
父线程的资源是父线程的,子线程无权共享
线程本身是没有父子关系的,也不存在所谓的主线程,只不过为了程序的编码方便,我们一般会引入父子线程来提高自身对程序的认知。线程中创建的线程他也是线程,需要锁的时候还是需要去竞争锁的使用权限。
package com.yang1;
public class Test04 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
S4.add();
}
}
}
class S4 {
private static final String suo = "suo";
private static int i = 0;
public static void add() {
synchronized (suo) {
i++;
System.out.println(i + "外部创建前" + Thread.currentThread());
new Thread() {
@Override
public void run() {
synchronized (suo) {
System.out.println(i + "内部-----" + Thread.currentThread());
}
}
}.start();
System.out.println(i + "外部创建后" + Thread.currentThread());
}
}
}

synchronized锁相比较于Lock接口而言,太重量级了,JDK1.6之引入了几款较为轻量级的锁机制。在竞争不严重的情况下,尽可能使用更少的成本实现,随着竞争的加剧,锁的状态会越来越重,单这个步骤是不可逆的。所在更替(下来、上去、销毁下来的)的时候也要成本的。
在很多情况下,虽然这个对象是线程安全的,但是很多情况下咱们用着线程安全的对象,但是用不到那么安全的对象(竞争没有那么激烈)。
1、一个线程来用的synchronized方法的时候:先加了一个偏向锁,单线程时,锁只会有一个标志(线程PID),用以存储这个锁的拥有者,除此以外,其他对象无法申请该锁(这个标志他会存储到 对象头的标识字mark word中)
2、当第二个线程来的时候他会升级成轻量级锁,这个锁在申请后在被申请的时候,竞争者会去做一个忙等操作(忙等不会释放CPU资源),用于占着CPU资源,锁的拥有者会去将偏向锁更替为轻量级锁。
3、在线程忙的的次数太高(达到某一阈值)的时候,轻量级锁会升级成重量级锁。(当线程之间还是交替进入临界区时,此时还是有序的,轻量级锁还凑合能用。等到多个线程同进入临界区的时候的次数太多了(忙等次数太多),这个锁就会升级成重量级锁)
乐观锁:
悲观锁:成本太高了
| 锁 | 描述 | 优点 | 缺点 | 应用场景 |
|---|---|---|---|---|
| 偏向锁 | 线程在大多数情况下并不存在竞争条件,使用同步会消耗性能,而偏向锁是对锁的优化,可以消除同步,提升性能。当一个线程获得锁,会将对象头的锁标志位设为01,进入偏向模式。偏向锁可以在让一个线程一直持有锁,在其他线程需要竞争锁的时候,锁会转变为轻量级锁 | 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距 | 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 | 适用于只有一个线程访问同步块的场景 |
| 轻量级锁 | 当线程A获得偏向锁后,线程B进入竞争状态,需要获得线程A持有的锁,那么线程A撤销偏向锁,进入无锁状态。线程A和线程B交替进入临界区,偏向锁无法满足,膨胀到轻量级锁,锁标志位设为00 | 竞争的线程不会阻塞,提高了程序的响应速度 | 如果始终得不到所竞争的线程,使用忙等会消耗CPU | 追求响应速度,同步块执行速度非常块 |
| 重量级锁 | 当多线程交替进入临界区,轻量级锁hold得住。但如果多个线程同时进入临界区,hold不住了,膨胀到重量级锁 | 线程竞争不使用忙等,不会消耗CPU | 线程阻塞,响应时间缓慢 | 追求吞吐量,同步块执行速度较慢 |
重量级锁:需要存储许多额外的数据和额外的CPU执行资源

对象头:一般包含
实例数据:
对齐字节: