黑马程序员_多线程(一)

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
多线程

一、多线程概述
进程:
是正在执行的程序。
是应用程序运行时的内存分配空间。
线程:
进程中的一个程序执行控制单元。
线程负责应用程序的执行顺序。
一个进程至少有一个进程。
多线程:
当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序。

二、创建线程的方式
创建线程的第一种方式,继承Thread类
步骤:1、定义类继承Thread
2、复写Thread类中run方法//将自定义代码存放在run中让线程运行
3、调用线程start方法//开启线程并执行该线程的run方法
      该方法的两个作用:启动线程,调用run方法
   run方法仅仅是对象调用方法,而线程创建了并没有运行

发现每一次的运行结果都不同,为什么
因为多个线程都获取cpu的执行权。cpu执行到谁谁就执行
明确一点,在某一刻时,只能有一个程序在运行(多核除外)
cpu在做着高速的切换以达到看上去同时运行的效果。

为什么要覆盖run方法呢?
Thread类用于描述线程
该类就定义了一个功能,用于储备线程要运行的代码。该存储功能就是run方法
也就是说Thread类中的run方法,用于储存线程要运行的代码。

d.star();//开启线程,并执行该线程的run方法
d.run();//仅仅是对象调用方法,而线程创建了并没有运行

线程都有自己默认的名称
Thread-编号 //该编号从0开始

static Thread currentThread():获取当前线程对象
getName():获取线程名称
setName():设置线程名称

创建线程的第二种方式:实现Runnable接口(常用方式)
步骤:1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法//将线程要运行的代码存放在该run方法中
3、通过Thread类建立线程对象
4、将Runnable接口的子类对象作为实际参数传递给Thread类构造函数
5、调用Thread类的star方法开启线程并调用Runnable接口子类的run方法。

为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定,指定对象的run方法,就必须明确该方法所属对象。

实现方法和继承方法的不同:
实现方式的好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码存放在接口子类run方法中

三、线程的运行状态



四、多线程的安全问题
class Ticket implements Runnable//定义类,实现Runnable接口
{
	private  int tick = 1000;
	public void run()//覆写run方法
	{
		while(true)
		{
			if(tick>0)
			{
				try{Thread.sleep(10);}catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
			}
		}
	}
}


class  TicketDemo2
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();//通过Thread建立线程对象

		Thread t1 = new Thread(t);//将Runnable接口的子类对象作为实参传递给Thread类构造函数
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		
		t1.start();//调用star方法
		t2.start();
		t3.start();
		t4.start();
	}
}

出现如下安全问题



问题的原因:
当多条语句当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行。导致共享数据的错误。

解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}


同步的前提:
1、必须要有两个或者两个以上的线程
2、必须要多个线程使用同一个锁
必须保证同步中只有一个锁在运行。
同步的好处:解决了多线程的安全问题。
同步的弊端:多个线程需要判断锁,较为消耗资源。

同步函数用的锁是 this

如果同步函数被static修饰,这时的同步函数用的是什么锁?
静态进入内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
静态的同步方法使用的锁是该方法所在类的字节码文件对象。类名.class

五、多线程中的单例设计模式
考点问题:
Q:写一个延时加载的单例模式
A:懒汉式

Q:饿汉式和懒汉式的区别
A:懒汉式是一种延迟加载的方式

Q:当出现多线程访问时应该怎么解决
A:加同步解决安全问题

Q:效率高么
A:不高

Q:怎么解决?
A:通过双重判断的形式解决

懒汉式代码如下
class Single
{
	private static Single s = null;
	private Single(){}
	public static  Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)//锁是字节码文件对象
			{
				if(s==null)
					s = new Single();
			}
		}
		return s;
	}
}

饿汉式没有安全问题,代码如下
class Single
{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}

六、死锁
死锁就是同步中嵌套同步,而锁却不同。导致程序卡住不能继续执行。
代码示例:
class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(MyLock.locka)//a锁
				{
					System.out.println("if locka");
					
					synchronized(MyLock.lockb)//b锁
					{
						System.out.println("if lockb");
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lockb)//b锁
				{
					System.out.println("else lockb");
					synchronized(MyLock.locka)//a锁
					{
						System.out.println("else locka");
					}
				}
			}
		}
	}
}
class MyLock//创建两个不同的锁
{
	static Object locka=new Object();
	static Object lockb=new Object();
}

class  DeadLock
{
	public static void main(String[] args) 
	{
		//创建并启动两个进程
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start(); 
		t2.start();
	}
}
运行结果:























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值