java学习笔记之多线程(二)

多线程同步

卖票实例

需求:每个线程卖100张票

class Ticket extends Thread
{ 
	private int num = 100; 
	public void run() 
	{
		 sale(); 
	} 
	public void sale() 
	{
		 while(true)
		  {
		  	 if(num > 0)
		  	  { 
		  	  	System.out.println(Thread.currentThread().getName()+“...sale...”num--); 
		  	  } 
		  } 
	}
 }
  class TicketDemo 
  {
  	 public static void main(String[] args) 
  	 { //四个对象,每个对象都有100张票 
  		 Ticket t1 = new Ticket();
  		 Ticket t2 = new Ticket(); 
  		 Ticket t3 = new Ticket(); 
  		 Ticket t4 = new Ticket(); 
  		 t1.start();
  		 t2.start(); 
  		 t3.start(); 
  		 t4.start(); 
  	} 
  }

需求:4个线程一起卖100张票

class Ticket implements Runnable 
{ 
	private int num = 100;
	public void run() 
	{ 
		sale();
	} 
	public void sale() 
	{
		while(true)
		{ 
			if(num > 0)
			{
				System.out.println(Thread.currentThread().getName()+“...sale...”num--);
			}
		}
	}
}
class TicketDemo
{
	public static void main(String[] args)
	{
		Ticket t = new Ticket(); //一个对象,所有一共只有100张票 
		Thread t1 = new Thread(t); 
		Thread t2 = new Thread(t); 
		Thread t3 = new Thread(t); 
		Thread t4 = new Thread(t); 
		t1.start(); 
		t2.start(); 
		t3.start(); 
		t4.start(); 
	}
}

线程安全问题的现象:

线程安全问题产生的原因:

  1. 多个线程在操作共享的数据;
  2. 操作共享数据的线程代码有多条;

当一个线程在执行操作共享数据的多条代码的过程中,其他线程参与了运算,就会导致线程安全问题的产生。

lock同步锁

解决思路:
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码时,其他线程是不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,同步代码块就可以解决这个问题。

同步代码块的格式:

synchronize(对象)
{
	需要被同步的代码
}

同步的好处:

解决了线程的安全问题。

同步的弊端:

相对降低了效率,因为同步外的线程都会判断同步锁。

同步的前提:

同步中必须有多个线程并使用同一个锁。

例:

class Ticket implements Runnable//extends Thread
{
	private  int num = 100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(num>0)
				{
					try{Thread.sleep(10);}catch (InterruptedException e){}
					System.out.println(Thread.currentThread().getName()
							+".....sale...."+num--);
				}
			}
		}
	}
}

同步函数

public synchronized void add(int num)
{
		sum = sum + num;
		try{Thread.sleep(10);}catch(InterruptedException e){}
		System.out.println("sum="+sum);
}

同步函数使用的锁是 this

同步函数和同步代码块的区别:
  1. 同步函数的锁是固定的this
  2. 同步代码块的锁是任意的对象

建议使用同步代码块
因为用this就固定了锁,没有灵活性。

验证静态同步函数的锁

静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class来表示。

多线程下的单例模式

如何保证对象唯一性?

  1. 不允许其他程序用new创建该类对象。
  2. 在该类中创建一个本类实例。
  3. 对外提供一个方法让其他程序可以获取该对象。

步骤:

  1. 私有化该类的构造函数。
  2. 通过New在本类中创建一个对象。
  3. 定义一个公有的方法,将创建的对象返回。
class Single
{
	private int num;
	public int getNum() 
	{
		return num;
	}
	public void setNum(int num) 
	{
		this.num = num;
	}
	//实现对象唯一性
	static Single s = new Single(); //创建本类类型
	private Single(){}//私有化构造函数
	public static Single getS()
	{	//获取
		return s;
	}
}
public class SingleDemo
{
	public static void main(String[] args)
	{	
		Single t1 = Single.getS();
		Single t2 = Single.getS();
		
		t1.setNum(10);
		t2.setNum(14);
		System.out.println(t1.getNum());
		System.out.println(t2.getNum());
	}
}

死锁

死锁常见于:

  1. 同步的嵌套
  2. 线程间通讯(多个线程在处理同一资源,但是任务却不同)

饿汉式:

class Single//类一加载,对象就已经存在了。
{
	private static Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}

懒汉式:

class Single2//类加载进来,没有对象,只有调用了getInstance方法时,才会创建对象。
		//延迟加载形式。 
{
	private static Single2 s = null;
	private Single2(){}
	public static Single2 getInstance()
	{
		if(s==null)
			s = new Single2();
		return s;
	}
}

等待/唤醒机制:
涉及的方法:

  1. wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
  2. notify():唤醒线程池中一个线程(任意).
  3. notifyAll():唤醒线程池中的所有线程。

这些方法都必须定义在同步中。因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。

为什么操作线程的方法wait notify notifyAll定义在了Object类中?

因为这些方法是监视器的方法。监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。

生产者/消费者模式(所生产者/多消费者问题)

while判断标记,解决了线程获取执行权后,是否要运行!
notifyAll解决了,本方线程一定会唤醒对方线程。
if判断标记,只有一次,会导致不该运行的程序运行了,出现了数据错误的情况。
notify()只能唤醒一个线程,如果本方唤醒了本方,没有意义,而且while判断标记+notify会导致死锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值