Java学习 多线程同步

我们使用多线程进行卖票,代码如下:

class Ticket implements Runnable
{
	private  int num = 50;
	public void run()
	{
		sale();	
	}
	public void sale()
	{
			while(true)
			{
				if(num>0)
				{
					try
					{
						Thread.sleep(10);			
					}
					catch(Exception e)
					{
						
					}
					System.out.println(Thread.currentThread().getName()+"..."+num--);
				}
			}
	}
}
public class Demo
{
	public static void main(String[] args) 
	{
		
		Ticket st = new Ticket();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		Thread t3 = new Thread(st);
		Thread t4 = new Thread(st);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
	}

}

运行结果:

Thread-1...50
Thread-3...48
Thread-2...49
Thread-0...47
Thread-2...46
Thread-3...45
Thread-1...46
Thread-0...44
Thread-2...43
Thread-0...42
Thread-1...43
Thread-3...43
Thread-0...41
Thread-2...38
Thread-3...40
Thread-1...39
Thread-1...37
Thread-2...37
Thread-0...37
Thread-3...37
Thread-3...36
Thread-2...34
Thread-1...33
Thread-0...35
Thread-0...32
Thread-2...30
Thread-3...29
Thread-1...31
Thread-2...28
Thread-1...27
Thread-0...28
Thread-3...28
Thread-2...26
Thread-0...25
Thread-1...25
Thread-3...26
Thread-2...24
Thread-3...22
Thread-0...23
Thread-1...24
Thread-2...21
Thread-0...20
Thread-1...20
Thread-3...20
Thread-3...19
Thread-1...18
Thread-2...16
Thread-0...17
Thread-3...15
Thread-1...15
Thread-0...14
Thread-2...13
Thread-3...12
Thread-1...12
Thread-0...11
Thread-2...10
Thread-3...9
Thread-1...9
Thread-0...8
Thread-2...7
Thread-1...6
Thread-3...6
Thread-2...5
Thread-0...4
Thread-1...3
Thread-3...3
Thread-2...2
Thread-0...2
Thread-1...1
Thread-3...0
Thread-2...-1
Thread-0...-2

我们发现最后三个分别是0 -1 -2 票的号码怎么能为负数和零呢,明显出现了问题。

首先多线程运行就是通过CPU随机在这几个线程之间进行切换,每个线程可能在运行某个代码是被剥夺运行权利,这就可能引发

线程同步安全问题

分析sale函数:

public void sale()
	{
			while(true)
			{
				if(num>0)        
				{
					try
					{
						Thread.sleep(10);			
					}
					catch(Exception e)
					{
						
					}
					System.out.println(Thread.currentThread().getName()+"..."+num--);
				}
			}
	}

假设当前num为20

当线程0进行if条件判断 20当然大于0 所以线程0进入了if语句中,但这时CPU剥夺线程0的运行资格,切换到了线程1,

线程1开始运行,此时num仍是20,线程1进行if判断 20大于0 满足if条件进入if语句,同样此时CPU剥夺了线程1的运行资格,

切换到了线程2.。。。

就这样所有的线程都进入了if语句中CPU切换到了线程0,线程0 1 2 3此时持有的num均为20

扫描二维码关注公众号,回复: 2581091 查看本文章

这样输出时可能会出现一张票会被多次购买或者出现负数票


这样的多线程是很不安全的,我们发现问题的根源就出在 当某个线程对共享资源进行处理时可能会被CPU暂停然后被其他线程

处理这个资源,这样容易引起混乱。

我们设想,如果当某个线程处理共享资源时,其他线程不能进入处理共享资源,这有当之前的线程处理完毕后,其他线程才能进入处理,好比是一把锁,线程拿到钥匙后才能进入,其他人没有钥匙无法进入,当有钥匙的人出来,将钥匙给其他人,其他人才能进入。

java为我们提供了同步代码块,在这个区间内只能有一个线程。

格式如下:

synchronized(锁对象){需要进行同步的代码块}


这样我们把之前的代码修改一下

class Ticket implements Runnable
{
	private  int num = 50;
	Object obj = new Object();    //作为同步锁
	public void run()
	{
		sale();	
	}
	public void sale()
	{
			while(true)
			{
				synchronized(obj)
				{
					if(num>0)
					{
						try
						{
							Thread.sleep(10);			
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"..."+num--);
					}
				}
			}
	}
}
public class Demo
{
	public static void main(String[] args) 
	{
		
		Ticket st = new Ticket();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		Thread t3 = new Thread(st);
		Thread t4 = new Thread(st);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
	}

}
Thread-0...50
Thread-3...49
Thread-3...48
Thread-3...47
Thread-3...46
Thread-3...45
Thread-3...44
Thread-3...43
Thread-3...42
Thread-3...41
Thread-3...40
Thread-3...39
Thread-3...38
Thread-3...37
Thread-3...36
Thread-3...35
Thread-3...34
Thread-3...33
Thread-3...32
Thread-3...31
Thread-3...30
Thread-3...29
Thread-3...28
Thread-3...27
Thread-3...26
Thread-3...25
Thread-3...24
Thread-3...23
Thread-3...22
Thread-3...21
Thread-3...20
Thread-3...19
Thread-3...18
Thread-3...17
Thread-3...16
Thread-3...15
Thread-3...14
Thread-3...13
Thread-3...12
Thread-3...11
Thread-3...10
Thread-3...9
Thread-3...8
Thread-3...7
Thread-3...6
Thread-3...5
Thread-3...4
Thread-3...3
Thread-3...2
Thread-3...1

除了同步代码块 还可以使用同步函数:

class Ticket implements Runnable
{
	private  int num = 50;
	Object obj = new Object();
	public void run()
	{
		//sale();
		while(true)
		{
			sale2();
		}
	}
	public synchronized void sale2()        //同步函数
	{
		if(num>0)
		{
			try
			{
				Thread.sleep(10);			
			}
			catch(Exception e)
			{
					
			}
			System.out.println(Thread.currentThread().getName()+"..."+num--);
		}
	}
	public void sale()            //使用同步代码块的函数
	{
			while(true)
			{
				synchronized(this)
				{
					if(num>0)
					{
						try
						{
							Thread.sleep(10);			
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"..."+num--);
					}
				}
			}
	}
}
public class Demo
{
	public static void main(String[] args) 
	{
		
		Ticket st = new Ticket();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		Thread t3 = new Thread(st);
		Thread t4 = new Thread(st);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
	}

}

同步函数的同步锁就是调用他的当前对象,就是this

因此我们在同步代码块中也可以不用创建对象作为同步锁,可以直接使用this


除了同步函数还有静态同步函数,静态同步函数的同步锁是 字节码文件对象 可以通过 对象名.getClass()

或者 类名.class 产生这个字节码文件对象



单例模式下的同步问题

class Single         //饿汉式 不存在安全问题
{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}


class Single         //懒汉式 存在安全问题
{
	private static  Single s = null;
	private Single(){}
	public static Single getInstance()
	{
		if(s==null)
			s = new Single();
		return s;
	}
}

需要加上同步锁

class Single         
{
	private static  Single s = null;
	private Single(){}
	public static synchronized Single getInstance()
	{
		if(s==null)
			s = new Single();
		return s;
	}
}



猜你喜欢

转载自blog.csdn.net/goddreamyyh/article/details/80661785