Java同步之synchronize


一,synchronized:基本使用规则

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的

详细说明:http://www.importnew.com/21866.html

这个博文里写的 比较详细,个人就不在去写了.代码案例也很丰富.

总结:synchronized实现同步必须是多个线程竞争同一个锁,这时才会实现同步,如果用的不是同一个锁,那是不会相互影响的.同时还要注意对象锁和类锁的区别.在使用的过程中同一个类可能会有很多对象锁,当不同线程使用不用对象锁时,是不会实现同步效果的.但是类锁的话就不同了,同一个类的类锁只有一个,所有对象都用的是同一个类锁,不同线程用到类锁时,都是同一个锁,这样就会实现互斥,实现同步了.

二,synchronized实现的复杂同步功能.sleep(),yield()join(),interrupt(),wait(),notify(),notifyall();

1,sleep方法:该方法是static和native修饰的本地静态方法,可抛出InterruptedException异常,传入参数long 毫秒;可直接通过类名加方法名调用.具体实现是通过jvm底层其他语言实现的.


使用:该方法调用后,是的该线程会进入睡眠状态,停止运行,但是线程是不会放弃持有的锁的.其他线程如果竞争该锁,是要继续等待的直到该线程睡眠时间结束,继续执行,到放弃锁时才能获得锁去运行实现同步.

2,yield()该方法是static和native修饰的本地静态方法.所以可直接通过类名加方法名调用

使用:该方法调用后,使得该线程放弃CPU资源的调用,重新进入待运行状态,和其他线程共同等待CPU进行调用分配资源.但是该方法不会使得线程失去持有的锁,除非该线程又进入运行状态后,放弃锁了,其他线程才能竞争锁.

3join()该方法是final修饰的方法,也就是该方法体是不会改变的.会抛出InterruptedException异常,里面是通过调用join(long millis)方法实现的.该方法是synchnized修饰的同步方法.里面通过调用wait()方法来实现线程的插入功能的.具体看源码:

总结:第一,主线程中调用子线程的jion方法时必须先让子线程启动,执行start方法,所以源码中isalive()会判断的有效.

第二,主线程中执行子线程的同步方法,住线程获取到子线程对象的锁,jion()中执行wait方法,使得主线程不停的在等待状态,直到子线程执行完毕

第三,while循环,自旋锁的使用.等解锁后jion方法执行完,子线程会调用exit()方法,执行里面的notifyall,唤醒主线程,使主线程执行jion后面的代码



4,wait()该方法是final和native修饰的本地方法,会抛出InterruptedException异常.方法是属于Object类的方法,由于Object类是超类,也就是说所有对象都可以使用该方法.

先说两个概念:锁池和等待池

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

首先该方法是在synchronized修饰的方法或代码块里使用的.在代码中调用该方法的必须是锁对象,不然运行会抛异常,获取不到监视器异常IllegalMonitorStateException.

其次:该方法调用后,使得线程进入该对象的等待池中,线程会进入阻塞状态,放弃持有的对象的锁.只有通过该对象的notify或notifyall 方法才能唤醒,进入该对象的锁池.等其他线程放弃该对象的锁时,它才有机会获得锁继续运行

5notify和notifyall方法;是final和native修饰的本地方法,就是该方法是不能修改的.

使用:都是要在synchronized修饰的方法或代码块里使用.和wait方法相同,如果不是同步代码块里的锁对象也会报异常IllegalMonitorStateException.

1,notify方法只会唤醒随机的一个等待池中的线程到锁池去重新竞争锁资源,notifyall会唤醒该锁对象的所有等待池的线程到锁池中去竞争锁资源.

6interrupt方法和interrupted和isinterrupted,这三个方法是一起讨论的:

interrupt() 向当前调用者线程发出中断信号

isinterrupted() 查看当前中断信号是true还是false

interrupted() 是静态方法,查看当前中断信号是true还是false并且清除中断信号,顾名思义interrupted为已经处理中断信号

使用时分两种情况一种是在线程中执行到wait,sleep,和jion方法使得线程处于阻塞状态时,使用这三个方法的情况,一个是没有上述三个方法的线程执行状况下使用这三个方法的情况.

先讨论第一种,首先我们要知道的是interrupt的方法是不会让线程的终止结束的,当某个线程对象执行interrupt方法时,这个线程会通过抛出异常来跳出线程的阻塞状态,进入到代码的catch部分,然后我们会根据业务需要再catch块中执行自己需要的代码块,也就是说线程还是在运行的,只是运行的是怎么处理跳出阻塞状态后的业务逻辑;案例中执行了interrupt方法后,worker线程还没有睡眠30秒,在收到中断信号后,立马抛出异常中断了睡眠状态,进入到catch代码块,而你通过isinterrupted方法去获取中断信号是其实结果是false.也就是说当在第一种情况下,执行interrupt方法时,中断信号是没有被底层接收,而是直接抛出异常了.

 public static void main(String[] args) throws Exception {  
	        Thread t = new Thread(new Worker());  
	        t.start();  
	        Thread.sleep(30); 
	        t.interrupt();
	        System.out.println("中断方法以执行.");  
	    }  
	      
	    public static class Worker implements Runnable {  
	        public void run() {  
	            System.out.println("Worker started.");  
	              try {
					Thread.sleep(300000);
				} catch (InterruptedException e) {
					 System.out.println("中断状态结束");
					 System.out.println(Thread.currentThread().isInterrupted());
				}
	        
	        }  
	    }  

下面讨论第二中情况,就是正常的线程执行过程中,没有上述三种方法执行导致的阻塞情况下执行interrupt方法的使用状况

public static void main(String[] args) throws Exception {
		Thread t = new Thread(new Worker());
		t.start();
		Thread.sleep(30);
		t.interrupt();
		System.out.println("中断方法以执行.");
	}

	public static class Worker implements Runnable {
		public void run() {
			while (!Thread.currentThread().isInterrupted()) {
				System.out.println("-------"+Thread.currentThread().isInterrupted());
			}
			
			boolean interrupted = Thread.interrupted();
			System.out.println(interrupted);
			System.out.println(Thread.currentThread().isInterrupted());
			Thread.currentThread().interrupt();
			System.out.println(Thread.currentThread().isInterrupted());
			System.out.println("中断状态结束");
			System.out.println("Worker stopped.");

		}

执行结果:
-------false
-------false
-------false
-------false
-------false
-------false
中断方法以执行.
true
false
true
中断状态结束
Worker stopped.

首先从案例中可以看出,线程启动是isinterrupted方法返回的是false,也就是没有接收到任何中断信号,但该线程执行了interrupt方法后,isinterrupt就返回true了,跳出了while循环.

第二步再执行interrupted方法后,我们看到返回的信息还是true,但是该方法已经把中断信息给去掉了,就相当于没有接受到中断信息了,我们在执行isinterrupted方法是又返回false了.

第三步,我们在执行该线程的interrupt方法,有给线程发一个中断信息,然后我们在获取中断状态时,通过isinterrupted方法得到的结果就是true了.

所以在第二种情况下,我们大体知道了这三个方法的使用了

总结:其实这三个方法作用就是想控制线程的运行节奏,让线程在开发者构思的过程中去运行,避免阻塞或长时间的损耗系统的性能.

一种是抛出异常的方式,一种通过boolean值来控制.都是由interrupt这个方法来发起信号.然后线程执行过程中根据信号的接受来做出是捕获异常去处理线程的逻辑,还通过Boolean值来处理业务逻辑.这样使得线程更加的灵活多变



猜你喜欢

转载自blog.csdn.net/zxf_0601/article/details/80054062