黑马程序员----多线程2



创建多线程的其中一种方式:继承Thread类,因为它有封装多线程的方法。

话说:这个类要想实现多线程就要继承Thread类是吧,那如果它自己有父类呢?

Runnable接口实现多线程机理,其实就是父类引用了子类的run方法

详解,null?


Thread继承了以后就获得了它里面的所有方法,但如果我们仅仅是想使用多线程来运行部分代码而已,那就实现Runnable



创建线程的第一种方式:继承Thread类。


创建线程的第二种方式:实现Runnable接口。


1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。


4,调用线程对象的start方法开启线程。




实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。


所以,创建线程的第二种方式较为常用。



卖票例子:

难点就是共享票的问题。(难道是静态的?因为静态变量有共享数据的功能嘛,静态是可以的)(不静态可不可以?)


不用静态变量,开启四次线程出现的异常!

这个是Runntime异常,一般这样的异常就是因为乱搞而产生的,你乱搞我就让你挂掉。


实现Runnable接口,调用thread方法可以解决这个问题。


仅对线程进行封装的好处!此处还是有点模糊num是怎么共享起来的。(注解:左边的程序跟图对不上的)


多线程的安全隐患,就说4条线程同时都过了if语句后,都具备执行资格,执行权一得到就会执行,有可能在执行到最后的时候,都已经进到if语句中,不再过判断了,那么就有可能输出超过if语句判断的语句,造成隐患。


想看异常情况:在if语句里面让线程睡一下。

这方法还带有抛异常,所以调用的时候得考虑到异常处理问题。

但是呀,run方法覆盖的是Runnable方法,而Runnable中的run方法并没有抛出异常,所以子类中的run方法也不能抛,只能try了。


问题产生的原因:多线程操作一个变量,如果不判断的话,一般不会出事。

线程安全隐患产生的原因:

1,多个线程在操作共享数据。

2,操作共享数据的线程代码有多条。

当一个线程在执行操作共享数据的多条代码中,其他线程也参加了运算,就会导致线程的安全隐患。(怎么解决呢)


多线程安全隐患解决思路:多条代码,多条线程执行时,把多条代码封装起来,其它线程要执行的时候判断完才让进去。

synchronized同步的意思。

同步代码块格式:

synchronized(对象)

{

需要被同步的代码;

}

 


可以了,但是有sleep慢了点。怎么实现问题解决的?


obj相当是一个标志位。就是说,1的时候相当于synchronized打开,0时就关闭。然后进去一个线程后,1就变成0,出来才变成。

对象锁被持有。为什么放个对象锁?后面再说,要监听什么的。。


同步的好处:解决了线程的安全问题。

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

(加了同步以后,效率会下降,原因是进去的线程不是一直都持有执行权,所以其它的线程获得执行权的时候也进不去。)


话说:如果用了同步以后还不行?需要保证前提!

同步的前提:同步中必须有多个线程并使用同一个锁。(怎么样会产生不同一个锁?把创建的对象锁放到run方法中,就会产生4个栈内存中的锁)



加法处出现线程安全隐患(需自己练习测试。)



class Bank
{

	private int sum;
	private Object obj = new Object();
	public void add(int num)
	{
		synchronized(obj)
		{
			sum = sum + num ;
	//		---->进程有可能会同时进到这个地方,就会出现安全隐患
	   // try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println("sum="+sum+"when="+Thread.currentThread().getName());
		}
	}
}

class Cus implements Runnable
{
	private Bank b = new Bank();
	public void run()// 切记放在run方法里面的东西要考虑到线程隐患的问题。
	{
		for(int x = 0; x<3 ; x++)
		{
			b.add(100);
		}
	}
}
class ThreadSafeDemo 
{
	public static void main(String[] args) 
	{
		//System.out.println("Hello World!");

		Cus c = new Cus();

		Thread t  = new Thread(c);
		Thread t1  = new Thread(c);
		//Thread t2  = new Thread(c);

		t.start();
		t1.start();
	//	t2.start();



	}
}



同步函数:例子中的的同步函数出现了只有一条线程运行的情况,因为把不该同步的部分给同步了,如:while()

解决办法:只抽出需要同步的部分进行封装成同步函数即可,在调用。哦了!


涉及线程切换:同步代码块 和 同步函数切换。(同步函数锁就是this),同步函数是同步代码块的简写模式,锁是固定的。

字节码所属文件对象:    

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


单例模式设计下的多线程安全隐患:

饿汉式

里面的创建对象函数只有一条语句,涉及不到多线程安全隐患问题。而懒汉式下,创建线程返回语句中存在共享数据 s,和共享语句s =new Single();,这样一来,多线程同时执行的时候就会产生安全隐患,怎么产生的呢?(具体一下?)

每次拿同步函数的时候都要判断对象锁效率低(这句话不是完全理解继续往下看看)

懒汉式的安全问题解决。多加if判断是提高效率的问题。(能完全解决程序)


死锁:同步嵌套,

直接卡死,为什么?原理:你拿我的锁,进你自己的锁,我拿你的锁,进我自己的锁,如果同时都拿着不释放,就进步去方法,就卡在锁的外面,就直接卡死。


/*
死锁:常见情景之一:同步的嵌套。

*/
class Ticket implements Runnable
{
	private  int num = 100;
	Object obj = new Object();
	boolean flag = true;
	public void run()
	{


		if(flag)
			while(true)
			{
				synchronized(obj)  // 拿着obj的锁,进this锁
				{
					show();
				}
			}
		else
			while(true)
				this.show();
	}

	public synchronized void show()//拿着this锁,进obj的锁。
	{

		synchronized(obj)
		{
			if(num>0)
			{
				try{Thread.sleep(10);}catch (InterruptedException e){}
				
				System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
			}
		}
	}
}


class DeadLockDemo 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();


		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		t.flag = false;
		t2.start();
	}
}



死锁程序运行结果(学会自己编写死锁程序,好接下来就是练习)


/*
1,需要两把锁护持
2,需要两个同步代码块

*/




class DeadLockFun implements Runnable
{
	private boolean flag; //线程切换标志位 
	DeadLockFun	(boolean flag)//初始化一下标志位,也可以不这么做,看喜好。
	{
		this.flag = flag;
	}
public void run() //线程封装,覆盖Runnable中的run方法
{
	if(flag)//ture线程就进这个同步代码块
	{
	//持有a锁进b锁。
	while(true)//让它自己跑吧,易观察打印锁死效果
		synchronized(MyLock.Lockb)
		{
			System.out.println(Thread.currentThread().getName()+"...Lockb");
			synchronized(MyLock.Locka)
			{
				System.out.println(Thread.currentThread().getName()+"...Locka");
			}
			
		}
	}
	else
		{
		//持有b锁进a锁。
			while(true)//让它自己跑吧,易观察打印锁死效果
			synchronized(MyLock.Locka)//静态对象直接通过类名调用 类名.对象
			{
				System.out.println(Thread.currentThread().getName()+"...Locka");
				synchronized(MyLock.Lockb)
				{
					System.out.println(Thread.currentThread().getName()+"...Lockb");
			
				}
				
			}
		}
	}
}

//两把锁
class MyLock
{
	public static final Object Locka = new Object();
	public static final Object Lockb = new Object();
}
class  DeadLock
{
	public static void main(String[] args) 
	{
		DeadLockFun d = new DeadLockFun(true);
		DeadLockFun d1 = new DeadLockFun(false);

		Thread t = new Thread(d);
		Thread t1 = new Thread(d1);

		t.start();
		t1.start();
		//也可以用下面的方法来创建
		/*
		DeadLockFun d = new DeadLockFun();
		Thread t = new Thread(d);
		t.start();
		t.setFlag(false);
		t.start();
		*/
		//System.out.println("Hello World!");
	}
}


好不容易才打印出多条(因为一下就锁死了。。。。)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值