由一个例子引入今天的内容
例子,创建三个窗口买票,总票数为100张,使用实现Runnable接口的方式
问题:卖票过程中,出现了重票、错票 --> 出现了线程安全问题
问题出现原因:当某个线程操作车票的过程中,尚未操作完成时,其它线程参与进来,也操作了该张票
如何解决:当一个线程在操作共享数据时,其他线程不能参与进来,直到线程A操作完共享数据,其他线程才可以开始操作
这种情况即使线程A出现了阻塞,也不能改变
在Java中,我们通过同步机制,来解决线程的安全问题
1.同步代码块
复制代码
1
2
3
4synchronized(同步监视器){ //需要被同步的代码 }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public class WindowTest1 { public static void main(String[] args) { Window1 window1 = new Window1(); Thread thread = new Thread(window1); Thread thread1 = new Thread(window1); Thread thread2 = new Thread(window1); thread.setName("线程1"); thread1.setName("线程2"); thread2.setName("线程3"); thread.start(); thread1.start(); thread2.start(); } } class Window1 implements Runnable { private int ticket = 100; Object object = new Object(); @Override public void run() { while (true) { // synchronized (object) { synchronized (this) { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket); ticket--; } else { break; } } } } }
说明:
- 操作共享数据的代码即为需要被同步的代码
- 共享数据:多个线程共同操作的变量,比如:ticket就是共享数据
- 同步监视器:俗称锁,任何一个类的对象都可以充当锁
- 要求:多个线程必须要共用同一把锁
- 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
2.同步方法
在需要同步的方法上添加synchronized关键字
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41public class WindowTest3 { public static void main(String[] args) { Window3 window3 = new Window3(); Thread thread1 = new Thread(window3); Thread thread2 = new Thread(window3); Thread thread3 = new Thread(window3); thread1.setName("线程1"); thread2.setName("线程2"); thread3.setName("线程3"); thread1.start(); thread2.start(); thread3.start(); } } class Window3 implements Runnable { private int ticket = 100; @Override public void run() { while (true) { show(); if (ticket <= 0) break; } } private synchronized void show() {//同步监视器:this if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket); ticket--; } } }
3.Lock锁 – JDK 5.0 新增
- 创建锁对象
- 在需要同步的代码块前调用lock()加锁
- 同步代码块后调用unlock()释放锁
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45class Window implements Runnable { private int ticket = 100; private ReentrantLock lock = new ReentrantLock(true);//1.创建锁对象 @Override public void run() { while (true) { try { lock.lock();//2.调用lock()加锁 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket); ticket--; } else { break; } } finally { lock.unlock();//3.调用unlock()释放锁 } } } } public class LockTest { public static void main(String[] args) { Window window = new Window(); Thread thread1 = new Thread(window); Thread thread2 = new Thread(window); Thread thread3 = new Thread(window); thread1.setName("线程1"); thread2.setName("线程2"); thread3.setName("线程3"); thread1.start(); thread2.start(); thread3.start(); } }
synchronized 与 lock的异同?
- 相同点:二者都可以解决线程安全问题
- 不同点:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
- lock需要手动的启动同步lock(),同时结束同步也需要手动的实现unlock()
4.总结
好处:同步的方式,解决了线程安全问题
坏处:操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低
使用的优先顺序:Lock ----> 同步代码块(已经进入了方法体,分配了相应资源) ----> 同步方法(在方法体之外)
最后
以上就是跳跃龙猫最近收集整理的关于线程的三种同步机制的全部内容,更多相关线程内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复