JavaSE——多线程同步的两大方式

实现多线程同步有两大方式——synchronized和lock

同步是指所有的线程不是一起进入到方法中执行,而是按照顺序一个一个进来的
一、 synchronized处理同步问题
1.同步代码块

使用同步代码块必须要设置一个要锁定的对象,一般为this

该方法进入到方法中的线程依然可能有多个,但是,进入到代码块中只能有一个线程
//使用同步代码块来实现多线程的同步
class MyThread implements Runnable
{
	private int ticket = 10; 
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<1000;i++)
		{
			//同一时刻,只允许一个线程进入代码块处理
			synchronized (this) {	//为程序逻辑上锁
				if(this.ticket>0)	//还有票
				{
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"还有"+this.ticket--+"张票");
					
				}
			}
		}
	}	
}
public class Test2
{
	public static void main(String[] args) {
		MyThread myThread=new MyThread();
		Thread thread1=new Thread(myThread,"A");
		Thread thread2=new Thread(myThread,"B");
		Thread thread3=new Thread(myThread,"C");
		thread1.setPriority(Thread.MIN_PRIORITY);
		thread2.setPriority(Thread.MAX_PRIORITY);
		thread3.setPriority(Thread.MAX_PRIORITY);
		thread1.start();
		thread2.start();
		thread3.start();
	}
}
2.同步方法

同步方法可以保证数据的完整性,即线程安全,但是同步方法的执行速度会很慢
//同步方法
class MyThread implements Runnable
{
	private int ticket = 100;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i < 100;i++)
		{
			this.sale();
		}
	}
	
	public synchronized void sale()
	{
		if(this.ticket > 0)
		{
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"还有"+this.ticket--+"张票");
		}
	}
	
}

public class Test2
{
	public static void main(String[] args) {
		MyThread mThread=new MyThread();
		Thread thread1=new Thread(mThread,"A");
		Thread thread2=new Thread(mThread,"B");
		Thread thread3=new Thread(mThread,"C");
		thread1.setPriority(Thread.MIN_PRIORITY);
		thread2.setPriority(Thread.MAX_PRIORITY);
		thread3.setPriority(Thread.MAX_PRIORITY);
		thread1.start();
		thread2.start();
		thread3.start();
	}
}

3. synchronized来锁多对象
由于synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段。即synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。而当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。因此要想使用 synchronized来锁住多对象有两种方法,第一种为锁住同一个对象,第二种为锁住这个类对应的Class对象
(1) synchronized锁住同一个对象
 
 
class Sync
{
	public void Test()
	{
		synchronized (this) {
			System.out.println("test方法的开始,当前线程为"+Thread.currentThread().getName());
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("test方法结束,当前线程为"+Thread.currentThread().getName());
		}
	}
}

class MyThread extends Thread
{
	private Sync sync;
	public MyThread(Sync sync)
	{
		this.sync=sync;
	}
	public void run() {
		this.sync.Test();	
	}
}

public class Test2
{
	public static void main(String[] args) {
		Sync sync=new Sync();
		for(int i=0;i<3;i++)
		{
			Thread thread=new MyThread(sync);
			thread.start();
		}
	}
}

(2) synchronized锁住这个类对应的Class对象
用synchronized(Sync.class)实现了全局锁的效果。因此,如果要想锁的是代码段,锁住多个对象的同一方法,使用全局锁,锁的是类而不是this。
class Sync
{
	public void Test()
	{
		synchronized (Sync.class) {
			System.out.println("test方法的开始,当前线程为"+Thread.currentThread().getName());
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("test方法结束,当前线程为"+Thread.currentThread().getName());
		}
	}
}

class MyThread extends Thread
{
	public void run() {
		Sync sync=new Sync();
		sync.Test();	
	}
}

public class Test2
{
	public static void main(String[] args) {
		for(int i=0;i<3;i++)
		{
			Thread thread=new MyThread();
			thread.start();
		}
	}
}
static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。

二、Lock锁解决同步
在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的Lock对象,性能更高一些。
到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
class MyThread implements Runnable
{
	private int ticket = 100;
	private Lock ticketLock=new ReentrantLock();
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i<100;i++)
		{
			ticketLock.lock();
			try {
				if (this.ticket > 0) 
				{ // 还有票
					try {
						Thread.sleep(20);
					}catch (InterruptedException e) {
						e.printStackTrace();
					} 
					System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + " 张票");
					}
			} finally {
			ticketLock.unlock();
			}
		}
	}	
}

public class Test2
{
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		Thread thread1= new Thread(myThread,"A");
		Thread thread2= new Thread(myThread,"B");
		Thread thread3= new Thread(myThread,"C");
		thread1.setPriority(Thread.MIN_PRIORITY);
		thread2.setPriority(Thread.MAX_PRIORITY);
		thread3.setPriority(Thread.MAX_PRIORITY);
		thread1.start();
		thread2.start();
		thread3.start();
		
	}
}

猜你喜欢

转载自blog.csdn.net/l_x_y_hh/article/details/80198335