我是靠谱客的博主 超级中心,这篇文章主要介绍java基础 day14数据安全,银行账户取款例子,synchronized()使用、面试题,死锁,线程安全,现在分享给大家,希望可以做个参考。

数据安全

  • 关于多线程并发环境下,数据的安全问题
  • 为什么这个是重点?以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写.
  • 最重要的是:你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据:在多线程并发的环境下是否是安全的。(重点:***** )
  • 什么时候数据在多线程并发的环境下会存在安全问题呢?
  • 三个条件:条件1:多线程并发,条件2:有共享数据,条件3:共享数据有修改的行为.
    满足以上3个条件之后,就会存在线程安全问题.
  • 怎么解决线程安全问题:线程同步机制
  • 线程排队执行,会牺牲一部分效率,但数据安全是第一位的
  • 异步编程模型:线程t1和线程t2,各自执行各自的,t1不管t2, t2不管t1, 谁也不需要等谁,这种编程模型叫做:异步编程模型。其实就是:多线程并发( 效率较高。)异步就是并发。
  • 同步编程模型:线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。效率较低。线程排队执行。同步就是排队。

synchronized()

编写程序,两个线程同时对一个账户进行取款操作。

Account类

复制代码
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package bankSystem; /** * 不适用线程同步机制,多线程对同一个银行账户取款,一定会出问题 使用线程同步机制,解决线程安全问题 */ public class Account { // 账号 private String actno; // 余额 private double balence; public Account() { } public Account(String actno, double balence) { super(); this.actno = actno; this.balence = balence; } public String getActno() { return actno; } public void setActno(String actno) { this.actno = actno; } public double getBalence() { return balence; } public void setBalence(double balence) { this.balence = balence; } // 取款的方法 /** * 以下代码,线程必须排队,不能并发 线程同步机制的语法块:synchronized(共享对象){} * 1、小括号里面的数据必须是多线程共享的数据,才能达到多线程排队 2、()内内容写什么:多线程共享的对象 那要看你想让哪些线程同步。 假设t1. t2. * t3. t4. t5 ,有个线程,你只希望t1 t2 t3排队, t4 t5不需要排B。怎么办? 这里共享对象是共账户对象,所以可以写this * */ /** * synchronized出现在实例方法上,一定锁的是this,所以这种方式不灵活。 * 还会导致整个方法体都想需要同步,程序执行效率降低,所以这种方法不常用 * 当锁的是this时,可以使用用来简化代码书写 * * @param money */ public synchronized void withdraw(double money) { //synchronized (this) { //synchronized (actno) { //在字符串常量池中,所有线程都会同步 //synchronized ("a") { // t1,t1是两个栈,并发执行这个方法,两个栈操作堆中的一个对象 // 取款之前的余额 double before = this.getBalence(); // 取款之后的余额 double after = before - money; // 模拟一下网络延迟1秒,一定会发生问题 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 更新余额 this.setBalence(after); //} } }

AccountThread类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package bankSystem; public class AccountThread extends Thread { // 连个线程必须共享同一个账户对象 private Account act; // 通过构造方法传递过来账户对象 public AccountThread(Account act) { super(); this.act = act; } // 执行取款操作 public void run() { // 假设取款5k double money = 5000; act.withdraw(money); System.out.println("对线程:" + Thread.currentThread().getName() + " 取款,账户:" + act.getActno() + " 取款成功,余额:" + act.getBalence()); } }

Test类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package bankSystem; public class Test { public static void main(String[] args) { Account act = new Account("act-001", 10000); Thread t1 = new AccountThread(act); Thread t2 = new AccountThread(act); // 设置name; t1.setName("t1"); // 设置t1优先级,默认优先级为5 t1.setPriority(8); t2.setName("t2"); // 启动线程取款 t1.start(); t2.start(); } }
  • 如果使用局部变量的话:建议使用: stringBuilder。因为局部变量不存在线程安全问题。选择stringBuilder。stringBuffer效率比较低。
    ArrayList是非线程安全的。
    Vector是线程安全的.
    HashMap,Hashset是非线程安全的.
    Hashtable是线程安全的.
  • synchronized()三种写法:
  • 第一种:同步代码块
复制代码
1
2
3
4
synchronized (线程共享对象) { 同步代码块; }
  • 第二种:.在实例方法上使用synchronized,表示共享对象一定是this
    并且同步代码块是整个方法体。
  • 第三种:在静态方法上使用synchronized表示找类锁。类锁永远只有1把,就算创建了100个对象,那类锁也只有一把。
    对象锁: 1个对象1把锁,100个对象100把锁.
    类锁: 100个对象,也可能只是1把类锁。
  • 面试题
    1、doOther()方法的执行需要等待doSome()方法执行结束吗?不需要,因为doOther()方法没有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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package threadSafe; public class Exam01 { public static void main(String[] args) { MyClass ma = new MyClass(); Thread t1 = new MyThread(ma); Thread t2 = new MyThread(ma); t1.setName("t1"); // t1.setPriority(7); t2.setName("t2"); t1.start(); // 保证t1线程先执行 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); } } class MyThread extends Thread { private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } @Override public void run() { // TODO Auto-generated method stub super.run(); if (Thread.currentThread().getName().equals("t1")) { mc.doSome(); } if (Thread.currentThread().getName().equals("t2")) { mc.doOther(); } } } class MyClass { //synchronized 出现在实例方法上,表示锁this public synchronized void doSome() { System.out.println("doSome begin!"); try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doSome over!"); } public void doOther() { System.out.println("doOther begin!"); System.out.println("doOther over!"); } } /** doSome begin! doOther begin! doOther over! doSome over! */

2、doOther()方法的执行需要等待doSome()方法执行结束吗?需要,因为doOther()方法有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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package threadSafe; public class Exam02 { public static void main(String[] args) { MyClass ma = new MyClass(); Thread t1 = new MyThread(ma); Thread t2 = new MyThread(ma); t1.setName("t1"); // t1.setPriority(7); t2.setName("t2"); t1.start(); // 保证t1线程先执行 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); } } class MyThread extends Thread { private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } @Override public void run() { // TODO Auto-generated method stub super.run(); if (Thread.currentThread().getName().equals("t1")) { mc.doSome(); } if (Thread.currentThread().getName().equals("t2")) { mc.doOther(); } } } class MyClass { public synchronized void doSome() { System.out.println("doSome begin!"); try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doSome over!"); } public synchronized void doOther() { System.out.println("doOther begin!"); System.out.println("doOther over!"); } }

3、doOther()方法的执行需要等待doSome()方法执行结束吗?需要,因为doOther()方法的this和doSome()的this指向不一样,对象有两个

复制代码
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
45
46
47
48
49
50
51
52
53
54
55
package threadSafe; public class Exam03 { public static void main(String[] args) { MyClass ma = new MyClass(); MyClass ma1 = new MyClass(); Thread t1 = new MyThread(ma); Thread t2 = new MyThread(ma1); t1.setName("t1"); // t1.setPriority(7); t2.setName("t2"); t1.start(); // 保证t1线程先执行 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); } } class MyThread extends Thread { private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } @Override public void run() { // TODO Auto-generated method stub super.run(); if (Thread.currentThread().getName().equals("t1")) { mc.doSome(); } if (Thread.currentThread().getName().equals("t2")) { mc.doOther(); } } } class MyClass { public synchronized void doSome() { System.out.println("doSome begin!"); try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doSome over!"); } public synchronized void doOther() { System.out.println("doOther begin!"); System.out.println("doOther over!"); } }

4、doOther()方法的执行需要等待doSome()方法执行结束吗?需要,因为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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package threadSafe; public class Exam04 { public static void main(String[] args) { MyClass ma = new MyClass(); MyClass ma1 = new MyClass(); Thread t1 = new MyThread(ma); Thread t2 = new MyThread(ma1); t1.setName("t1"); // t1.setPriority(7); t2.setName("t2"); t1.start(); // 保证t1线程先执行 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); } } class MyThread extends Thread { private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } @Override public void run() { // TODO Auto-generated method stub super.run(); if (Thread.currentThread().getName().equals("t1")) { mc.doSome(); } if (Thread.currentThread().getName().equals("t2")) { mc.doOther(); } } } class MyClass { public synchronized static void doSome() { System.out.println("doSome begin!"); try { Thread.sleep(1000 * 10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("doSome over!"); } public synchronized static void doOther() { System.out.println("doOther begin!"); System.out.println("doOther over!"); } }

死锁

示例:

复制代码
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package threadSafe; /** * 死锁代码要会写。一般面试官要求你会写。 * 只有会写的,才会在以后的开发中注意这个事儿。 因为死锁很难调试 */ public class DeadLock { public static void main(String[] args) { Object o1 = new Object(); Object o2 = new Object(); Thread t1 = new MyThread1(o1, o2); Thread t2 = new MyThread2(o1, o2); t1.start(); t2.start(); } } class MyThread1 extends Thread { Object o1; Object o2; public MyThread1(Object o1, Object o2) { super(); this.o1 = o1; this.o2 = o2; } @Override public void run() { // 死锁,synchronized在开发过程中,最后不要嵌套使用,很有可能造成死锁 synchronized (o1) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (o2) { } } super.run(); } } class MyThread2 extends Thread { Object o1; Object o2; public MyThread2(Object o1, Object o2) { super(); this.o1 = o1; this.o2 = o2; } @Override public void run() { synchronized (o2) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (o1) { } } super.run(); } }

如何解决线程安全问题:

  • 一上来就使用synchronized吗?
  • 不是,synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择线程同步机制。
  • 第一种方案:尽量使用局部变量代替"实例变量和静态变量”。
  • 第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题了。)
  • 第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized了。线程同步机制.

最后

以上就是超级中心最近收集整理的关于java基础 day14数据安全,银行账户取款例子,synchronized()使用、面试题,死锁,线程安全的全部内容,更多相关java基础内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部