Synchronized理解与测试

1.synchronized关键字说明

对于操作系统进程来说,资源分配是很重要的一个问题,如果操作不当会造成多个进程抢占同一个资源,进而造成死锁,无限等待。线程同样无法避免这个问题,对于资源的控制与分配,Java可以使用Synchronzied同步
(注:避免进程死锁的四个必要条件(任何一个不成立死锁就会发生):

  • 互斥条件:每个资源同时只能有一个进程访问。
  • 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

synchronized用法
  • synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。
  • 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁。
  • 每个对象只有一个锁(lock)与之相关联。

2.测试

(1)没有加锁的情况

有一个打印Print类,PrintNumber方法打印数字,PrintChar打印字母。
两个线程,都有一个Print类变量。一个打印字母,一个打印数字

class PrintTest {
	public void PrintNumber() {
		for (int i = 0; i < 20; i++) {
			System.out.println(i);
		}
	}

	public  void PrintChar() {
		for (int i = 0; i < 20; i++) {
			System.out.println((char) (i+65));
		}
	}
}

class mthread12 extends Thread {
	public PrintTest test;
	public mthread12(PrintTest test) {
		// TODO Auto-generated constructor stub
		this.test=test;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		test.PrintNumber();
	}
}
class mthread13 extends Thread {
	public PrintTest test;
	public mthread13(PrintTest test) {
		// TODO Auto-generated constructor stub
		this.test=test;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		test.PrintChar();
	}
}

在主方法中new 一个Print类对象,两个线程都引用这一个对象。

public class thread2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		PrintTest test=new PrintTest();
		mthread12 mthread12=new mthread12(test);
		mthread13 mthread13=new mthread13(test);
		mthread12.start();
		mthread13.start();
	}

}

输出结果:
在这里插入图片描述
打印数字线程还没有执行完,打印字母线程就使用了打印对象。对象资源没有加锁。

(2)一个方法加锁的情况

现在在Print类的打印数字方法加锁

public synchronized void PrintNumber() {
		for (int i = 0; i < 20; i++) {
			System.out.println(i);
		}
	}

	public void PrintChar() {
		for (int i = 0; i < 20; i++) {
			System.out.println((char) (i+65));
		}
	}

执行结果
在这里插入图片描述
两个线程同时执行。虽然调用了同一个对象,并且对象的某个方法加了锁,但是其他线程依然可以访问该对的没有加锁的代码块而不受阻塞。

(3)两个方法同时加锁

给两个方法同时加锁

public synchronized void PrintNumber() {
		for (int i = 0; i < 20; i++) {
			System.out.println(i);
		}
	}

	public synchronized void PrintChar() {
		for (int i = 0; i < 20; i++) {
			System.out.println((char) (i+65));
		}
	}

输出结果
在这里插入图片描述
先打印数字后再打印字母,在打印数字时打印字母线程不能访问对象而受阻塞。当线程访问一个对象的synchronized方法,其他的线程不能访问该对象加了synchronized的其他方法。

(4)使用同类不同对象

现在构造线程时使用不同的Print对象

public static void main(String[] args) {
		// TODO Auto-generated method stub
		mthread12 mthread12=new mthread12(new PrintTest());
		mthread13 mthread13=new mthread13(new PrintTest());
		mthread12.start();
		mthread13.start();
	}

打印结果
在这里插入图片描述
同步执行。此时synchronized针对的是同一个对象,而不是同一个类。

5、给对象加锁

Print对象方法都不加锁

public void PrintNumber() {
			for (int i = 0; i < 20; i++) {
				System.out.print(i);
			}
	}

	public void PrintChar() {
		for (int i = 0; i < 20; i++) {
			System.out.print((char) (i + 65));
		}
	}

在两个线程的run方法里加入synchronized

@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized (test) {
			test.PrintNumber();
		}
	}
@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized (test) {
			test.PrintChar();
		}
	}

运行结果
在这里插入图片描述
运行结果要么是先打印完数字,要么先打印完字母。后面的线程会被阻塞。拿到test对象锁的线程可以执行被synchronized包围的代码块。

(6)给类加锁

把Print类的两个方法都加上锁并且改为静态方法

class PrintTest {
	public synchronized static void PrintNumber() {
			for (int i = 0; i < 20; i++) {
				System.out.print(i);
			}
	}

	public synchronized static void PrintChar() {
		for (int i = 0; i < 20; i++) {
			System.out.print((char) (i + 65));
		}
	}
}

两个线程run方法的synchronized去掉

@Override
	public void run() {
		// TODO Auto-generated method stub
		test.PrintNumber();
	}
@Override
	public void run() {
		// TODO Auto-generated method stub
		test.PrintChar();
	}

主方法new 两个不同的对象

public static void main(String[] args) {
		// TODO Auto-generated method stub
		//PrintTest test=new PrintTest();
		mthread12 mthread12 = new mthread12(new PrintTest());
		mthread13 mthread13 = new mthread13(new PrintTest());
		mthread12.start();
		mthread13.start();
	}

输出结果
在这里插入图片描述
同时访问该类的不对对象也会被阻塞。在static修饰的方法加锁,作用域是整个类,而不是这个类的某一个对象。

总结

1.synchronized可以修饰方法和一段代码块。

 void synchronized method(){

}

等价于

void method(){
	synchronized(this){

	}
}

修饰一个代码块时,只有拿到()里的对象的锁,才能执行{}里的代码。
2.在静态方法里加锁,是锁定了这个类,而不是该类的对象

void synchronized static method(){

}

等价于

void  method(){
	synchronized(this.getClass){

	}
}

注:在线程拿到锁之后,执行sleep()休眠时,不管休眠时间多久,都不会释放该线程拿到的锁。

3.修饰代码块时,可以更加精细的控制同步,拿到o1的锁时执行一段,拿到o2的锁时执行一段。一个对象只有一把钥匙一把锁,一个线程拿到后,其他线程都必须等待释放后抢占。当线程访问一个对象的synchronized方法,其他的线程不能访问该对象加了synchronized的其他方法,但是可以随意访问没有加synchronized的方法。

void method(){
	synchronized(Object o1){

	}
	synchronized(Object o2){

	}
}

synchronized简单测试
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值