多线程之线程安全关键字synchronized

3 篇文章 0 订阅
2 篇文章 0 订阅

    synchronized关键字,是多线程编程时保证线程安全使用非常广泛的java知识。下面我们学习下synchronized的相关知识:

实现原理

    synchronized的实现原理是基于内存中的lock原则。内存模型中的变量读写有八个步骤也有八个原则,synchronized的实现是基于八个原则中的lock原则。将其实现在指令级别就是monitorenter和monitorexit。编写测试用例如下图1-1,采用同步块的方式在testJJJ()方法中,将其编译后执行javap -c MyService1.class,输出如下图1-2

                                                                                         图 1-1

                                                                                              图1-2

monitor是监控器,java虚拟就为每个对象生成一个监控器,并放置在对象头中。每个线程需要获取到该监控器才可以进入synchronized控制的范围以内的代码进行执行,否则会被放置在一个监控器等待池中,直到占有该监控器的线程执行完代码后释放监控器后,系统会以一定的策略从等待池中的线程中取出一个线程去获得该监控器,然后执行该段代码。

使用方法

    从使用位置上分为同步方法和同步块。同步方法指的是synchronized关键字出现在方法的签名上,如下:

同步块,如下:

       同步方法是将整个方法作为一段同步代码处理,任何时候只有一个线程获取了该对象锁才可以执行这个方法,而同步块是将{}中的代码作为一段同步代码处理的。因为同步虽然保证了线程的安全,但是降低了并发性,所以业务逻辑允许的范围内,同步的代码范围越小越好。

从使用的监控器来说,可以分为一般的对象和Class对象(虽然Class的父类也是Object,但是Class的类的对象我们可以认为是一类特殊的对象)。

    一般对象的监控器,只是对该对象下的所有实例同步方法起作用。Class对象的监控器是对该类的所有静态方法也就是类方法起作用。示例如下:

public class Main implements Runnable {

	// private Boolean falg = false;
	public static void main(String[] args) {
		Main main = new Main();
		Thread aThread = new Thread(main);
		Thread bThread = new Thread(main);
		Thread cThread = new Thread(main);
		Thread dThread = new Thread(main);
		aThread.start();
		bThread.start();
		cThread.start();
		dThread.start();
	}

	@Override
	public void run() {
		hi();
	}

	public void hi() {

		System.out.println(Thread.currentThread().getName() + " run...");
		System.out.println(Thread.currentThread().getName() + "hi thread!");

	}

}

    说明:Main类实现了Runnable接口,重写了run方法,该方法调用了hi方法,hi方法中有两行打印输出代码。

   1.没有在hi方法中加synchronized:

    

结果分析:运行结果非线程安全的。

2.在hi方法中加synchronized:

public class Main implements Runnable {

	// private Boolean falg = false;
	public static void main(String[] args) {
		Main main = new Main();
		Thread aThread = new Thread(main);
		Thread bThread = new Thread(main);
		Thread cThread = new Thread(main);
		Thread dThread = new Thread(main);
		aThread.start();
		bThread.start();
		cThread.start();
		dThread.start();
	}

	@Override
	public void run() {
		hi();
	}

	public synchronized void hi() {

		System.out.println(Thread.currentThread().getName() + " run...");
		System.out.println(Thread.currentThread().getName() + "hi thread!");

	}

}

结果分析:

线程安全。

3.将线程的创建方式修改后代码如图:

public class Main implements Runnable {

	// private Boolean falg = false;
	public static void main(String[] args) {
		Thread aThread = new Thread(new Main());
		Thread bThread = new Thread(new Main());
		Thread cThread = new Thread(new Main());
		Thread dThread = new Thread(new Main());
		aThread.start();
		bThread.start();
		cThread.start();
		dThread.start();
	}

	@Override
	public void run() {
		hi();
	}

	public synchronized void hi() {

		System.out.println(Thread.currentThread().getName() + " run...");
		System.out.println(Thread.currentThread().getName() + "hi thread!");

	}

}

运行结果如图:

结果分析:运行结果不是线程安全的。原因是因为每个线程获取的监控器都不同,所以相当于同步没有起作用。只需要在hi方法中加上static关键字即可。

修改后运行结果如图:

结果分析:synchronized修饰的static方法,则监控锁为Main.class ,而该对象只有一个,并且所有Main calss的实例化对象公用一个类方法,所以同一时刻只有一个线程能进入该方法。如果将该同步方法修改为同步块,并将监控的对象该为Main.class,则效果一样,但是原理不一样。

4.再次验证如上结论,修改如下:

public class Main implements Runnable {

	// private Boolean falg = false;
	public static void main(String[] args) {
		Main main = new Main();
//		Thread aThread = new Thread(main);
//		Thread bThread = new Thread(main);
//		Thread cThread = new Thread(main);
//		Thread dThread = new Thread(main);
		Thread aThread = new Thread(new Main());
		Thread bThread = new Thread(new Main());
		Thread cThread = new Thread(new Main());
		Thread dThread = new Thread(new Main());
		aThread.start();
		bThread.start();
		cThread.start();
		dThread.start();
		
		
	}

	@Override
	public void run() {
		hi();
		bye();
	}

	public static synchronized void hi() {

		System.out.println(Thread.currentThread().getName() + " hi run...");
		System.out.println(Thread.currentThread().getName() + " hi thread!");

	}

	public synchronized void bye() {
		System.out.println(Thread.currentThread().getName() + " bye run...");
		System.out.println(Thread.currentThread().getName() + " bye run run...");
		System.out.println(Thread.currentThread().getName() + " bye thread!");

	}
}

 

每个线程中传入不同的Main对象,在线程中调用的run方法中执行hi和bye方法,hi方法为静态的,bye方法不是静态的。执行后结果如下:

bye方法并非线程安全的,但是hi方法是线程安全的,虽然两个方法都加了synchronized。如果给bye方法加上static关键字,则结果为线程安全的。

5.在Main类中添加实例方法test,并加上synchronized关键字,代码如下:

public class Main implements Runnable {

	// private Boolean falg = false;
	public static void main(String[] args) {
		Main main = new Main();
		Thread aThread = new Thread(main);
		Thread bThread = new Thread(main);
		Thread cThread = new Thread(main);
		Thread dThread = new Thread(main);
//		Thread aThread = new Thread(new Main());
//		Thread bThread = new Thread(new Main());
//		Thread cThread = new Thread(new Main());
//		Thread dThread = new Thread(new Main());
		aThread.start();
		bThread.start();
		cThread.start();
		dThread.start();
		
		
	}

	@Override
	public void run() {
		hi();
		bye();
		test();
	}

	public static synchronized void hi() {

		System.out.println(Thread.currentThread().getName() + " hi run...");
		System.out.println(Thread.currentThread().getName() + " hi thread!");

	}

	public synchronized void bye() {
		System.out.println(Thread.currentThread().getName() + " bye run...");
		System.out.println(Thread.currentThread().getName() + " bye run run...");
		System.out.println(Thread.currentThread().getName() + " bye thread!");

	}
	public synchronized void test() {
		System.out.println(Thread.currentThread().getName() + " test run...");
		System.out.println(Thread.currentThread().getName() + " test run run...");
		System.out.println(Thread.currentThread().getName() + " test thread!");

	}
}

运行后结果如下:

Thread-1 hi run...
Thread-1 hi thread!
Thread-3 hi run...
Thread-3 hi thread!
Thread-3 bye run...
Thread-3 bye run run...
Thread-3 bye thread!
Thread-3 test run...
Thread-3 test run run...
Thread-3 test thread!
Thread-1 bye run...
Thread-2 hi run...
Thread-2 hi thread!
Thread-0 hi run...
Thread-0 hi thread!
Thread-1 bye run run...
Thread-1 bye thread!
Thread-0 bye run...
Thread-0 bye run run...
Thread-0 bye thread!
Thread-0 test run...
Thread-0 test run run...
Thread-0 test thread!
Thread-2 bye run...
Thread-2 bye run run...
Thread-2 bye thread!
Thread-2 test run...
Thread-2 test run run...
Thread-2 test thread!
Thread-1 test run...
Thread-1 test run run...
Thread-1 test thread!

结果显示为线程安全的,也就是说非static的线程安全方法,只能同时一个线程进入其中的一个方法。

6.将test方法中的同步关键字去掉,重新运行程序,会出现如下的结果:

Thread-0 hi run...
Thread-0 hi thread!
Thread-0 bye run...
Thread-3 hi run...
Thread-3 hi thread!
Thread-0 bye run run...
Thread-2 hi run...
Thread-2 hi thread!
Thread-0 bye thread!
Thread-1 hi run...
Thread-1 hi thread!
Thread-2 bye run...
Thread-0 test run...
Thread-2 bye run run...
Thread-2 bye thread!
Thread-2 test run...
Thread-2 test run run...
Thread-0 test run run...
Thread-2 test thread!
Thread-3 bye run...
Thread-0 test thread!
Thread-3 bye run run...
Thread-3 bye thread!
Thread-3 test run...
Thread-3 test run run...
Thread-1 bye run...
Thread-1 bye run run...
Thread-1 bye thread!
Thread-3 test thread!
Thread-1 test run...
Thread-1 test run run...
Thread-1 test thread!

也就是该test方法并非线程安全的,同一时刻有可能有多个线程同时进入该方法。例如如上日志显示,Thread-0在执行期间Thread-2进入了该方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值