java同步方法、线程状态

同步即是同一时间不同线程访问同一数据。要想实现同步操作,必须获得每一个对象的锁。获得它可以保证在同一时刻只有一个线程能进入临界区(访问互斥资源的代码段),并在这个锁被释放之前,其他线程就不能再进入这个临界区,必须进入等待队列,等待该锁被当前占用线程释放(即拥有该锁的线程退出临界区),高优先级的线程将获得锁。 对象的锁也称为监视器

实现同步的方法:

1、Synchronized  

1)synchronized修饰方法

需要被同步的资源可以放到方法中,这样就可以实现同步。主要要注意实例方法和静态方法。

注意synchronized得到的是对象锁,一旦某个方法加了这个锁,某一个线程通过调用这个方法得到了这个对象锁,其他线程就无法获得这个对象的锁,即便调用的是其它synchronized修饰的方法,也必须等当前占用这个对象锁的线程先释放锁。但美甲synchronized的方法在其他线程被调用时是可以被调用的,因为它不需要得到锁。用测试代码来看:

package java_IO;

public class sTest {
	synchronized public void t1() {
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
	synchronized public void t2() {
		for(int i=100;i<200;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
	public void t3() {
		for(int i=200;i<300;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

测试代码:

package java_IO;

public class synchronizedTest {

	public static void main(String[] args) {
		sTest st=new sTest();
		Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.t1();
			}
		});
		t1.start();
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.t2();
			}
		});
		 t2.start();
		 
		 Thread t3=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.t3();
			}
		});
		 t3.start();
		 
		 Thread t4=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.t1();
			}
		});
		 t4.start();
		// TODO Auto-generated method stub

	}

}

结果就是t1和t3交叉输出,然后t4和t3交叉,最后t2(这只是一种情况)

静态方法和实例方法基本一模一样,只是实例是在对象上加锁,静态是在类上加锁。但是注意类锁和对象锁不是同一把锁,两个运行之间互不干扰。

package java_IO;

public class sTest {
	synchronized public static void t1() {
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
	synchronized public void t2() {
		for(int i=100;i<200;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
	public void t3() {
		for(int i=200;i<300;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
package java_IO;

public class synchronizedTest {

	public static void main(String[] args) {
		sTest st=new sTest();
		Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.t1();
			}
		});
		t1.setName("Thread1");
		t1.start();
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.t2();
			}
		});
		t2.setName("Thread2");
		t2.start();

	}

}

 结果就是Thread1和Thread2交叉运行。

2)synchronized修饰代码块

synchronized(obj){

}

可以将需要同步的对象放入其中,仍然就是得到对象的锁,这样能提高利用效率

如下代码

package java_IO;

public class sTest {
	Object o=new Object();
	public void t3() {
		synchronized (o) {
			for(int i=0;i<10;i++) {
				System.out.println(Thread.currentThread().getName()+":"+o+""+i);
			}
			
		}
	}
	public void t4() {
		synchronized (this) {
			for(int i=0;i<10;i++)
				System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}


	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
package java_IO;

public class synchronizedTest {

	public static void main(String[] args) {
		sTest st=new sTest();
		Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				st.t3();
				// TODO Auto-generated method stub
				
			}
		},"Thread1");
		t1.start();
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				st.t4();
				// TODO Auto-generated method stub
				
			}
		},"Thread2");
		t2.start();
	}

}

结果:

Thread1:java.lang.Object@5405ed040
Thread2:0
Thread1:java.lang.Object@5405ed041
Thread1:java.lang.Object@5405ed042
Thread2:1
Thread2:2
Thread2:3
Thread1:java.lang.Object@5405ed043
Thread2:4
Thread1:java.lang.Object@5405ed044
Thread1:java.lang.Object@5405ed045
Thread2:5
Thread1:java.lang.Object@5405ed046
Thread1:java.lang.Object@5405ed047
Thread1:java.lang.Object@5405ed048
Thread2:6
Thread1:java.lang.Object@5405ed049
Thread2:7
Thread2:8
Thread2:9
2、wait和notify

wait就是释放锁,不再占用该锁,notify就是唤醒一个线程(等待队列中的第一个),若没有notify,则之前wait的线程会一直等待,即阻塞,不会往下运行。注意线程的五种状态:新建(New)、可运行(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)

1. 新建(NEW):新创建了一个线程对象。

2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种: 

(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

简单举例如下

package java_IO;

public class sTest {
	synchronized public void waitTest(){
		
		try {
			System.out.println("wait start....");
			this.wait();
			System.out.println("wait end...");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	synchronized public void notifyTest() {
		
		System.out.println("notify start...");
		this.notify();
		System.out.println("notify end...");
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
package java_IO;

public class wait_notify_Test {

	public static void main(String[] args) {
		sTest st=new sTest();
		 Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				st.waitTest();
				// TODO Auto-generated method stub
				
			}
		},"waitTestThread");
		t1.start();
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				st.notifyTest();
				// TODO Auto-generated method stub
				
			}
		},"notifyTestThread");
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();
		
		
		// TODO Auto-generated method stub

	}

}

结果:

wait start....
notify start...
notify end...
wait end...


interrupt
interrupt()来自于Thread类,用途是中断线程。如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。

3、Lock

主要是:ReentrantLock类,ReentrantReadWriteLock类,结构图如下

Lock相对于Synchronized方法好处:

1、 有condition这个选项,可以分情况释放

和通知其他线程占用,Synchronized相当于就是只有一个condition,其await相当于synchronized的wait,signal相当于notify。

代码如下

package java_IO;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class sTest {
	Lock lock=new ReentrantLock();
	Condition c1=lock.newCondition();
	Condition c2=lock.newCondition();
	
	public void c1Await() {
		lock.lock();
		try {
			System.out.println(Thread.currentThread().getName()+":condition1 await start");
			c1.await();
			System.out.println(Thread.currentThread().getName()+":condition1 await end");
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	public void c2Await() {
		lock.lock();
		try {
			System.out.println(Thread.currentThread().getName()+":condition2 await start");
			c2.await();
			System.out.println(Thread.currentThread().getName()+":condition2 await end");
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	public void c1SignalAll() {
		lock.lock();
		System.out.println(Thread.currentThread().getName()+":condition1 signal start");
		c1.signalAll();
		//c1.sinalAll();
		System.out.println(Thread.currentThread().getName()+":condition1 signal end");
		lock.unlock();
	}
	public void c2SignalAll() {
		lock.lock();
		System.out.println(Thread.currentThread().getName()+":condition2 signal start");
		c2.signalAll();
		//c1.sinalAll();
		System.out.println(Thread.currentThread().getName()+":condition2 signal end");
		lock.unlock();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
package java_IO;

public class conditionTest {

	public static void main(String[] args) {
		sTest st=new sTest();
		Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.c1Await();
			}
		},"Thread1");
		Thread t3=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.c2Await();
			}
		},"Thread3");
            t1.start();
		t3.start();
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Thread t2=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.c1SignalAll();
			}
		},"Thread2");
		t2.start();
		
		Thread t4=new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				st.c2SignalAll();
			}
		},"Thread4");
		t4.start();
		
		// TODO Auto-generated method stub

	}

}

结果:

Thread1:condition1 await start
Thread3:condition2 await start
Thread2:condition1 signal start
Thread2:condition1 signal end
Thread4:condition2 signal start
Thread4:condition2 signal end
Thread1:condition1 await end
Thread3:condition2 await end

lock还有以下几种方法

1)tryLock():以非阻塞的方式获取锁。只是尝试去获得以下锁,得到了就立即返回true,没得到就立即返回false,

2)tryLock(long timeout,TimeUnit unit):规定一个时间等待,若在这个时间内获得了锁则返回true,超过这个时间则返回false;

3)lockInterruptibly(): 如果获得了锁,立即返回;若没有获得锁,该线程会休眠直到得到锁,或者当前线程被中断(会收到InterruptedException)。它与lock()最大的区别是,lock方法得不到锁会一直处于阻塞状态,会忽略interrupt()中断线程的方法。

猜你喜欢

转载自blog.csdn.net/qq_33605294/article/details/88542235