想要解决多线程问题,首先我们需要掌握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;
}
}
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:代码块锁中的锁,可以灵活变换,可以锁定其他实体,也可以锁定该片段为类的锁,使该类的多实体多线程抢用同一锁,其他线程处于等待状态。
4:代码块锁在一个线程抢占资源后,其他线程处于等待状态,而并不是该线程执行完之后其他线程才可执行,当一个线程for循环执行一个代码块锁。在执行一次后会释放锁,然后共同抢锁,有可能出现for循环只执行了一次,锁的资源被其他线程抢走了。所以在处理业务逻辑要特别注意。