现在的位置: 首页 > 综合 > 正文

java 多线程 同步 synchronized 的个人理解和用法

2019年07月29日 ⁄ 综合 ⁄ 共 4973字 ⁄ 字号 评论关闭

1.当 synchronized A方法被一个线程调用的时候(运行过程中), 另外一个线程调用A方法会block住,而并不是请求失败,如果此时在block住的线程实例上调用interrupt方法就会触发InterruptedException,然后请求的序列会被cache在请求队列中,在队列中的顺序并不一定是你代码从上到下的运行顺序,而是未知的,这个队列可以cache住的任务数量可以很大,
直到耗光所有内存为止。

public class Test_synchronized {

	private int a = 0;

	public static void main(String[] args) {
		final Test_synchronized sy = new Test_synchronized();
		for (int i = 0; i < 99; i++) {
			final int tmp = i;
			new Thread(new Runnable() {

				@Override
				public void run() {
					sy.A(tmp);
				}
			}).start();
		}
		//当控制台输出 --->end的时候 就意味着所有的线程都开始运行了
		System.out.println("----> end");
	}
	
	//改变共享变量 a 这个方法会sleep 3 秒钟 
	public synchronized void A(int num) {
		try {
			Thread.sleep(0);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		a = num;
		System.out.println(a);
	}

}

运行结果:

0
2
3
1
4
5
6
7
8
----> end
9

2. synchronized 不能直接锁定一个变量(private synchronized int c 这个是错误的),因为java的同步时基于对象的对象锁(官方叫法为监视器),而变量不是对象,这里应该这样理解,Object o = new Object(); o 是指向一定对象的,也就是“=” 右边的东西是对象, 而a只是一个占用4个字节的指针所以a并不是对象,那么也就不能锁住变量了。
但是如果用synchronized(变量){}这里可以用变量,因为这里是一个引用,就是指针指向的那个对象。

3. synchronized obj{ ... }  其中obj就是对象锁,这个对象锁可以理解为是一个标记,那为什么要用对象而不用普通类型做标记,因为java是基于对象的语言,即使同一个类的不同对象实例也会拥有不同的hashcode,也就是唯一的标示,就像身份证一样。

synchronized 方法运行的时候,JVM会读对象锁的计数器,如果为0就可以放行,如果不为0就要等别人释放这个方法的对象锁。

其实可以理解为对象锁可以和synchronized 方法没有任何关系,对象锁仅仅只是一个标记,并且这个标记必须为对象,就像是你每回去电影院看电影都会买到一张不同的票,票是完整的就证明没看过,检票后,票会被撕开一部分,就证明这张票看过了。

所以 synchronized 只要传入的是一样的对象锁,那么就可以锁住不同的代码块,字符串也行,就是 "" 这样的空字符串也行。

public class Test_synchronized_2 {
	
	//如果运行结果都是一般长的就代表线程同步了,如果出现b --- > thread id == 1 2a ---> thread id == 8 1这样的结果
	//就证明a方法走到了print后还没有运行println, b方法就运行print了,就是说是费线程同步的	
	public static void main(String[] args) {
		final Test_synchronized_2 sy = new Test_synchronized_2();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					sy.a(1);
				}
			}
		}).start();
		while(true) {
			sy.b(2);
		}
	}
	
	public void a(int flag) {
		synchronized("123") {
			//注意这里不会输出换行符哦~!这是print和println的区别啦
			System.out.print("a ---> thread id == " + Thread.currentThread().getId() + " " + flag);
			//注意这里会自动输出换行符哦
			System.out.println();
		}
	}
	
	public void b(int flag) {
		synchronized("123") {
			System.out.print("b --- > thread id == " + Thread.currentThread().getId() + " " + flag);
			System.out.println();
		}
	}

}

运行结果:

a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2

这里有容易犯错的地方,"123"实际上是创建的匿名对象,那就是说a和b方法里锁住的对象锁不一样啦,因为每次都创建了一个新对象,相当于2个"123"对象,那为什么还能同步呢,是以为java里有String池的概念,所以"123"都是从池子里拿出来的同一个对象,只要在第一次时是创建,后面就是取了。如果把"123"换成new String("123");就不会线程同步了

public class Test_synchronized_2 {
	
	//如果运行结果都是一般长的就代表线程同步了,如果出现b --- > thread id == 1 2a ---> thread id == 8 1这样的结果
	//就证明a方法走到了print后还没有运行println, b方法就运行print了,就是说是费线程同步的	
	public static void main(String[] args) {
		final Test_synchronized_2 sy = new Test_synchronized_2();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					sy.a(1);
				}
			}
		}).start();
		while(true) {
			sy.b(2);
		}
	}
	
	public void a(int flag) {
		synchronized(new String("123")) {
			//注意这里不会输出换行符哦~!这是print和println的区别啦
			System.out.print("a ---> thread id == " + Thread.currentThread().getId() + " " + flag);
			//注意这里会自动输出换行符哦
			System.out.println();
		}
	}
	
	public void b(int flag) {
		//换成new String("123"
		synchronized(new String("123")) {
			System.out.print("b --- > thread id == " + Thread.currentThread().getId() + " " + flag);
			System.out.println();
		}
	}

}

运行结果:

b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2a ---> thread id == 8 1

b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1
a ---> thread id == 8 1b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2
b --- > thread id == 1 2

4.对于synchronized 的非 static 方法而已,java编译器隐式的用了this这个对象锁, 对于synchronized 的 static 方法而言,编译器隐式用了 方法所在类的class对象作为对象锁。

5.如果一个类里面的方法都是synchronized的,即都是用的this对象锁的时候,那么当一个方法执行的时候另外一个方法就只有等的份了,可以理解为所有方法都是一个互斥的整体,一个上,其他的就得全部等。如果我又一个类,我想要里面的所有方法之对自己是同步的,对别人不影响,就是说a方法被调用时,其他线程调用a方法时就block住了,但是还可以调用b方法。也就是单方法的同步。有什么场景是需要使用这样的功能的呢? 当我取钱的时候账户里只有100元,当我调用取钱方法a取100时,其他人就不能同时调用a方法取钱了,要不然就会取出多余账户余额的数目,而当我取的时候,显然账户还可以同时接收网购给我的退款,存和取互不影响,但是取和存自己是互斥的。

public class Test_synchronized_3 {

	public static void main(String[] args) {
		final Test_synchronized_3 sy = new Test_synchronized_3();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					sy.a();
				}
			}
		}).start();
		
		while(true) {
			sy.a();
		}
	}

	//如果出现11 或者 111或者更多1在同一行,证明该方法没有被锁住.
	public void a() {
		synchronized("1") {
			System.out.print("1");
			System.out.println();
		}
	}

}

运行结果:

1
1
1
1
1
1
1
1

运行结果全部是单行1,即a方法同一时间只能被同一线程调用。如果去掉synchronized就会出现11或者111或者更多1在同一行的情况

如果增加b方法且是用和a方法一样的对象锁,那么a和b方法就同步,同一时间只能有一个线程运行a和b其中之一,如果a和b的对象锁不一样, 那么就不会互斥,就可能出现12或者21或者其他的很多数在同一行的情景.

public class Test_synchronized_3 {

	public static void main(String[] args) {
		final Test_synchronized_3 sy = new Test_synchronized_3();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					sy.a();
				}
			}
		}).start();
		
		while(true) {
			sy.b();
		}
	}

	//如果出现11 或者 111或者更多1在同一行,证明该方法没有被锁住.
	public void a() {
		synchronized("1") {
			System.out.print("1");
			System.out.println();
		}
	}

	public void b() {
		synchronized("2") {
			System.out.print("2");
			System.out.println();	
		}
	}
}

运行结果:

1
1
1
1
1
2
2
21
1
1
2
2
1

这样就实现了 方法自身同步 方法之间不同步的效果。

JAVA 5 以后增加了 Lock的类  实现类似于过程化的同步功能。以后研究研究在聊聊, synchronized理解错误的地方希望大家指正,不要骂我就好~

抱歉!评论已关闭.