想要解决多线程问题,首先我们需要掌握Java锁的原理。
我们常用锁Synchronized的方式
一:方法锁
1:方法锁锁的是该对象。例如:
public static void main(String[] arr) { final Test1 test1 = new Test1(); for (int i = 0; i < 15; i++) { new Thread(new Runnable() { @Override public void run() { test1.send(); } }).start(); } }
public class Test1 { int data; public synchronized void send() { System.out.println(); System.out.println("方法入口******= "+ Thread.currentThread().getName()); for (int i = 0; i < 20000; i++) { data++; } System.out.println(data); System.out.println("方法出口&&&&&&= "+ Thread.currentThread().getName()); data = 0; } }
输出结果为:
说明线程处于排队状态,该方法只允许一个线程执行,执行完之后释放锁,线程重新获取锁执行。
2:去掉send方法上的锁 synchronized关键字
public class Test1 { int data; public void send() { for (int i = 0; i < 20000; i++) { data++; } System.out.println(data); data = 0; } }
扫描二维码关注公众号,回复:
1676733 查看本文章
public class Test1 { int data; public void send() { System.out.println(); System.out.println("方法入口******= "+ Thread.currentThread().getName()); for (int i = 0; i < 20000; i++) { data++; } System.out.println(data); System.out.println("方法出口&&&&&&= "+ Thread.currentThread().getName()); data = 0; } }
输出结果为
由此可见,去掉锁,则其中一个线程在执行该方法时,其他线程依然可以执行。
3:当多个实例调用send方法时
public static void main(String[] arr) { for (int i = 0; i < 15; i++) { final Test1 test1 = new Test1(); new Thread(new Runnable() { @Override public void run() { test1.send(); } }).start(); } }
public class Test1 { int data; public void send() { System.out.println(); System.out.println("方法入口******= "+ Thread.currentThread().getName()); for (int i = 0; i < 20000; i++) { data++; } System.out.println(data); System.out.println("方法出口&&&&&&= "+ Thread.currentThread().getName()); data = 0; } }
输出结果为:
所以由此说明,方法锁,锁的是当前实例。当有多个实例,锁不受影响。
二:代码块锁
该方式的锁,比较灵活。既可以锁当前实体,也可以锁定当前类。锁定当前实体,则跟方法锁一样,这里不再说明。重点说下锁定类。
1:锁定类。当锁定类的时候,不论是单一实体还是多实体的多线程请求该段上锁的代码块,都将抢锁,其他线程处于等待。
public static void main(String[] arr) { for (int i = 0; i < 10; i++) { final Test1 test1 = new Test1(); new Thread(new Runnable() { @Override public void run() { test1.send(); } }).start(); } }
public class Test1 { int data; public void send() { synchronized (Test1.class){ System.out.println(); System.out.println("方法入口******= "+ Thread.currentThread().getName()); for (int i = 0; i < 20000; i++) { data++; } System.out.println(data); System.out.println("方法出口&&&&&&= "+ Thread.currentThread().getName()); data = 0; } } }
输出结果为:
由此说明,锁定类,即使是多实体的多线程,依然是有效果的。
总结:
1:当一个类中有多个方法锁时,具有方法锁的方法则彼此间不能互相调用,会出现死锁现象。
2:代码块锁中的锁,可以灵活变换,可以锁定其他实体,也可以锁定该片段为类的锁,使该类的多实体多线程抢用同一锁,其他线程处于等待状态。
3:代码块锁在一个线程抢占资源后,其他线程处于等待状态,而并不是该线程执行完之后其他线程才可执行,当一个线程for循环执行一个代码块锁。在执行一次后会释放锁,然后共同抢锁,有可能出现for循环只执行了一次,锁的资源被其他线程抢走了。所以在处理业务逻辑要特别注意。