为什么要进行线程同步?
java允许多线程并发控制,当多个线程同时操作一个可共享资源变量时(如对其进行增删改查操作),会导致数据不准确,而且相互之间产生冲突。所以加入同步锁以避免该线程在没有完成操作前被其他线程调用,从而保证该变量的唯一性和准确性。
不同步会发生的问题?
在介绍同步方法之前先演示一下当多个线程操作一个共享资源时可能会发生的错误,这里用的方法是让线程在执行时睡眠10毫秒,会导致多个线程去操作同一个资源变量:
1 public class SynTest {
2 public static void main(String[] args) {
3 //定义三个线程,
4 MySyn ms = new MySyn();
5 Thread t1 = new Thread(ms,"线程1输出:");
6 Thread t2 = new Thread(ms,"线程2输出:");
7 Thread t3 = new Thread(ms,"线程3输出:");
8 t1.start();
9 t2.start();
10 t3.start();
11 }
12 }
13
14 class MySyn implements Runnable{
15
16 int tick = 10; //共执行10次线程
17 public void run() {
18 while(true){
19 if(tick>0){
20 try {
21 Thread.sleep(10); //执行中让线程睡眠10毫秒,
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 System.out.println(Thread.currentThread().getName() + " " + tick--);
26 }
27 }
28 }
29 }
输出结果用以下图片展示,可以看到我勾选的部分都发生了冲突数据:
同步方法1:
同步函数:就是用synchronize关键字修饰的方法。因为每个java对象都有一个内置锁,当用synchronize关键字修饰方法时内置锁会保护整个方法,而在调用该方法之前,要先获得内置锁,否则就会处于阻塞状态。
代码演示:请将上方代码的第17行改为以下代码↓
public synchronized void run() {}
同步方法2:
同步代码块:就是拥有synchronize关键字修饰的语句块,被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。
代码演示:将上方代码的run方法改成下方代码
public void run() {
while(true){
synchronized (this) { //同步代码块
if(tick>0){
try {
Thread.sleep(10); //执行中让线程睡眠10毫秒,
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + tick--);
}
}
}
}
加同步之后的输出数据为:
线程1输出: 10
线程2输出: 9
线程2输出: 8
线程2输出: 7
线程2输出: 6
线程2输出: 5
线程2输出: 4
线程3输出: 3
线程3输出: 2
线程3输出: 1
追加问题:如果同步函数被静态修饰之后,使用的锁是什么?静态方法中不能定义this!
静态内存是:内存中没有本类对象,但是一定有该类对应的字节码文件对象。 类名.class 该对象类型是Class。
所以静态的同步方法使用的锁是该方法所在类的字节码文件对象。 类名.class。代码如下:
public static mySyn(String name){
synchronized (Xxx.class) {
Xxx.name = name;
}
}