1、应用之互斥
为了避免临界区竞态条件的发生,有多种手段可以达到目的
- 阻塞式:synchronized,Lock
- synchronized:Java关键字
- Lock(锁)为接口,有多个实现类
- 非阻塞式:非原子变量
这里我们使用阻塞式的解决方案:synchronized,来解决多线程安全问题,既常说的锁对象。它使用互斥的方式保证同一时刻最多只有一个线程能持有对象锁,其他线程想要获取这个对象锁时会阻塞住。这样能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心上下文切换。
2、synchronized简单应用
-
创建简单计数器类
-
成员变量:count,初始值0
-
成员方法:自增、自减、读取count的值
-
代码:
public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized void decrement() { count--; } public synchronized int getCount() { return count; } }
-
-
测试类:
@Slf4j(topic = "security.syn.TestCounter") public class TestCounter { public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread t1 = new Thread(() -> { for (int i = 0; i < 500; i++) { counter.increment(); } }, "t1"); Thread t2 = new Thread(() -> { for (int i = 0; i < 500; i++) { counter.decrement(); } }, "t2"); t1.start(); t2.start(); t1.join(); t2.join(); log.debug("最终计数:{}", counter.getCount()); } }
当方法上不加synchronized时,结果不可预测。加上synchronized后,最终结果:0
3、锁对象
那么synchronized锁对象到底是对那个对象加锁了呢?
- 静态方法:类对象
- 成员方法:this对象
- 普通代码块:参数里面的对象
下面以几个示例分析锁的是哪个对象,给出输出结果及分析锁对象,
-
输出结果:
- 1 2
- 2 1
- 锁对象:this
@Slf4j(topic = "security.syn.LockObject") public class LockObject { public static void main(String[] args) { Number1 n = new Number1(); new Thread(() -> { log.debug("begin"); n.a(); }).start(); new Thread(() -> { log.debug("begin"); n.b(); }).start(); } } @Slf4j(topic = "security.syn.Number1") public class Number1 { public synchronized void a() { log.debug("1"); } public synchronized void b() { log.debug("2"); } }
-
输出结果
- 等1s 1 2,图示:
-
2 等1s 1,图示:
-
锁对象:this
@Slf4j(topic = "security.syn.Number2")
public class Number2 {
public synchronized void a() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
}
-
输出结果
- 方法c没加synchronized,方法a加了sleep;可以确定的是3一定先与1输出
- 可能的情况:3->1s->1->2;3 ->2->1s-> 1;2->3->1s->1
- 锁对象:this
@Slf4j(topic = "security.syn.LockObject1") public class LockObject { public static void main(String[] args) { Number3 n = new Number3(); new Thread(() -> { log.debug("begin"); n.a(); }).start(); new Thread(() -> { log.debug("begin"); n.b(); }).start(); new Thread(() -> { log.debug("begin"); n.c(); }).start(); } } @Slf4j(topic = "security.syn.Number3") public class Number3 { public synchronized void a() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("1"); } public synchronized void b() { log.debug("2"); } public void c() { log.debug("3"); } }
-
输出结果:
- 因为是2个不同的对象,不存在互斥
- 2->1s->1
- 锁对象:this
@Slf4j(topic = "security.syn.LockObject") public class LockObject { public static void main(String[] args) { Number4 n1 = new Number4(); Number4 n2 = new Number4(); new Thread(() -> { log.debug("begin"); n1.a(); }).start(); new Thread(() -> { log.debug("begin"); n2.b(); }).start(); } } @Slf4j(topic = "security.syn.Number4") public class Number4 { public synchronized void a() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("1"); } public synchronized void b() { log.debug("2"); } }
-
输出结果:
- 方法a静态方法,锁对象:类对象
- 方法b成员方法,锁对象:this
- 不存在互斥,输出 2->1s->1
@Slf4j(topic = "security.syn.LockObject") public class LockObject { public static void main(String[] args) { Number5 n = new Number5(); new Thread(() -> { log.debug("begin"); n.a(); }).start(); new Thread(() -> { log.debug("begin"); n.b(); }).start(); } } @Slf4j(topic = "security.syn.Number5") public class Number5 { public static synchronized void a() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("1"); } public synchronized void b() { log.debug("2"); } }
-
分析:
- 方法a,b都是静态方法,锁对象:同一类对象
- 不管new几个对象,都存在互斥关系,输出同1
@Slf4j(topic = "security.syn.LockObject") public class LockObject { public static void main(String[] args) { Number6 n1 = new Number6(); Number6 n2 = new Number6(); new Thread(() -> { log.debug("begin"); n1.a(); }).start(); new Thread(() -> { log.debug("begin"); n2.b(); }).start(); } } @Slf4j(topic = "security.syn.Number5") public class Number6 { public static synchronized void a() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("1"); } public static synchronized void b() { log.debug("2"); } }