首页 > 代码库 > Java---14---多线程---synchronized 同步函数
Java---14---多线程---synchronized 同步函数
设计这样一个程序:两个用户去银行存钱,每次存100,存3次,显示银行中总的钱数。
class Bank { private int sum;//sum代表银行中钱的总量 public void add (int n) { sum += n; System.out.println("sum = "+sum); } } class Cus implements Runnable { private Bank b = new Bank(); @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 3; i++) { b.add(100); } } } public class TONGBUHANSHU { public static void main(String[] args) { Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
那么该程序有没有安全问题呢?多运行几次发现
输出的结果可能是:
sum = 100
sum = 300
sum = 400
sum = 200
sum = 500
sum = 600
说明该程序存在安全问题。如何找到问题?
1.明确哪些代码是多线程运行代码
run方法和add方法
2.明确共享数据
b 和 sum
3.明确多线程运行代码中哪些语句是操作共享数据的
class Bank { private int sum; Object obj = new Object(); public void add (int n) { synchronized(obj) { try { Thread.sleep(100); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } sum += n; System.out.println(" sum = "+sum); } } } class Cus implements Runnable { private Bank b = new Bank(); @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 3; i++) { //System.out.print(Thread.currentThread().getName()); b.add(100); } } } public class TONGBUHANSHU { public static void main(String[] args) { Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
经过同步代码块之后再运行就不会在出现上面的情况了。
同步代码块的作用就是封装代码,而函数的作用也是封装代码,那么函数封装代码和同步代码块封装代码有什么不同?
唯一的区别就是:同步代码块封装的代码带有同步的特性。
那如果让函数具备了同步的特性?
将 synchronized 关键字作为修饰符放到函数上
这就是传说中的同步函数:class Bank { private int sum; Object obj = new Object(); public synchronized void add (int n) { //synchronized(obj) // { try { Thread.sleep(100); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } sum += n; System.out.println(" sum = "+sum); // } } } class Cus implements Runnable { private Bank b = new Bank(); @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 3; i++) { //System.out.print(Thread.currentThread().getName()); b.add(100); } } } public class TONGBUHANSHU { public static void main(String[] args) { Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
将之前卖票的例子也搞成同步函数的形式。
class Test implements Runnable { private int num = 50; Object obj = new Object(); public synchronized void run () { while (true) { // synchronized (obj) //{ if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">>"+num--); } //} } } } public class RUNNABLE { public static void main (String[] args) { Test t = new Test(); Thread a = new Thread(t); Thread b = new Thread(t); Thread c = new Thread(t); Thread d = new Thread(t); a.start(); b.start(); c.start(); d.start(); } }
然而输出结果显示全部都是0线程运行的,其他线程没有运行。
为什么?没有弄清楚哪些代码是需要同步的。
需要同步的代码是同步代码块之中的代码,而现在直接将synchronized 关键字作为修饰符放到run方法上,显然与之前用同步代码块的方式时同步的代码是不同的。
解决:
将同步代码块之中的代码封装成函数。
class Test implements Runnable { private int num = 50; // Object obj = new Object(); public void run () { while (true) { show (); } } public synchronized void show () { // synchronized (obj) //{ if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">>"+num--); } //} } } public class RUNNABLE { public static void main (String[] args) { Test t = new Test(); Thread a = new Thread(t); Thread b = new Thread(t); Thread c = new Thread(t); Thread d = new Thread(t); a.start(); b.start(); c.start(); d.start(); } }
同步函数的锁是哪个?
函数需要被对象调用,函数都有一个所属对象的引用
所以同步函数使用的锁是this。
验证:
使用两个线程来卖票,一个线程在同步代码块中
一个线程在同步函数中,都在执行卖票动作
如果同步的话是不会出现错误
class Test implements Runnable { private int num = 50; Object obj = new Object(); boolean flag = true; public void run () { if (flag) { while (true) { synchronized (obj) { if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">>--code-- "+num--); } } } } else { while (true) { show(); } } } public synchronized void show ()// 锁是 ? { // synchronized (obj) //{ if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">> show "+num--); } //} } } public class RUNNABLE { public static void main (String[] args) { Test t = new Test(); Thread a = new Thread(t); Thread b = new Thread(t); a.start(); try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception } t.flag = false; b.start(); } }
两个线程,a线程运行的是同步代码块中的内容,b线程运行的同步函数中的内容。
输出结果显示有-1号票
说明这个程序时不安全的。
这个程序中已经使用了同步,但是却还是有错误?
考虑使用同步的前提条件:
1.必须有两个或两个以上的线程--(满足)
2.必须使用同一个锁--(??)
同步代码块中的锁是obj,而同步函数中的锁肯定不是obj,我们假设同步函数的锁是this,将同步代码块中的锁也换成this试试。
class Test implements Runnable { private int num = 50; Object obj = new Object(); boolean flag = true; public void run () { if (flag) { while (true) { synchronized (this) { if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">>--code-- "+num--); } } } } else { while (true) { show(); } } } public synchronized void show ()// 锁是 this { // synchronized (obj) //{ if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">> show "+num--); } //} } } public class RUNNABLE { public static void main (String[] args) { Test t = new Test(); Thread a = new Thread(t); Thread b = new Thread(t); a.start(); try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception } t.flag = false; b.start(); } }
发现输出结果中没有-1号票了,说明该程序安全,也就说明了同步函数的锁是this
静态同步函数的锁是什么?
将show方法声明为静态方法,为了编译通过同时将num声明为静态
class Test implements Runnable { private static int num = 50; Object obj = new Object(); boolean flag = true; public void run () { if (flag) { while (true) { synchronized (this) { if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">>--code-- "+num--); } } } } else { while (true) { show(); } } } public static synchronized void show ()// 锁是 this { // synchronized (obj) //{ if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">> show "+num--); } //} } } public class RUNNABLE { public static void main (String[] args) { Test t = new Test(); Thread a = new Thread(t); Thread b = new Thread(t); a.start(); try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception } t.flag = false; b.start(); } }
显示结果有-1,程序不安全,说明了静态同步函数锁使用的锁不是this;
静态方法中没有隐含的this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名 .class 该对象的类型是class
class Test implements Runnable { private static int num = 50; Object obj = new Object(); boolean flag = true; public void run () { if (flag) { while (true) { synchronized (Test.class) { if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">>--code-- "+num--); } } } } else { while (true) { show(); } } } public static synchronized void show ()// 锁是 this { // synchronized (obj) //{ if (num >= 0) { try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } System.out.println(Thread.currentThread().getName()+">> show "+num--); } //} } } public class RUNNABLE { public static void main (String[] args) { Test t = new Test(); Thread a = new Thread(t); Thread b = new Thread(t); a.start(); try { Thread.sleep(20); } catch (Exception e) { // TODO: handle exception } t.flag = false; b.start(); } }
程序运行安全,说明静态同步的方法,使用的锁是该方法所在类的字节码文件对象:类名.class
点击打开链接
Java---14---多线程---synchronized 同步函数