黑马程序员--Java基础--多线程(一)

------- android培训java培训、期待与您交流! ----------

第一部分:多线程概述‘

1、进程:正在执行的程序。

线程:进程中一个负责程序执行的控制单元(执行路径)。

2、每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。如果有多个执行路径,那就是多线程。

而线程在控制这进程的的执行。

一个进程中至少会有一个线程

3、多线程的好处:解决了多部分代码同时运行的问题。
多线程的弊端:线程太多,会导致效率的降低。
其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花
费时间的,从而导致了效率的降低。

注:
JVM启动时启动了多条线程,至少有两个线程可以分析的出来:
1.执行main函数的线程,该线程的任务代码都定义在main函数中。
2.负责垃圾回收的线程。


第二部分:创建线程

java已经提供了对线程这类事物的描述,就是Thread类。通过Thread类来创建线程。

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

步骤:
a、定义类继承Thread
b、复写Thread类中的run方法
目的:将自定义的代码存储在run方法中,让线程运行。
c、调用线程的start方法,该方法两个作用:启动线程,调用run方法

代码示例:

class Demo extends Thread
{
	public void run()
	{
		System.out.println("demo run");
	}
}
class  
{
	public static void main(String[] args) 
	{
		Demo d = new Demo();//创建好线程
		d.start();
		System.out.println("Hello World!");
	}
}
注:

a、通过对发现每次的运行结果都不相同,这是因为多个线程都在获取cpu执行权,cpu执行到谁,谁就运行。因此应该明确一点,在某一时刻只能运行一个线程(多核除外)。cpu在做着快速切换,已达到看上去是同时运行的效果。

我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。

这体现了多线程的一个特性:随机性。

b、为什么要覆盖run方法?

Thread类是用来描述线程的。该类定义了一个run方法来存储线程要运行的代码。

调用start方法可以开启线程并执行该线程的run方法,而d.run()只是对象调用方法,线程并没有创建。


2、获取线程对象以及名称。

每个线程都有自己的默认名称,格式为:Thread-编号(编号从0开始)。

关键api:

获取当前线程:static Thread currentThread()。

获取线程名称:getName()。

设置线程名称:setName()或者使用构造函数构造函数super(String name)。

<pre name="code" class="java">//以下三句等效。
this.getName();
Thread.currentThread().getName()
currentThread().getName()

 
调用Thread.currentThread()返回的是线程对象。 

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

步骤:

a、定义类实现Runnable接口b、覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中

c、通过Thread类建立线程对象d、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数e、调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法。

代码示例(以售票为例子

//第一步:实现接口Runnable
class Ticket implements Runnable
{
	private static int tick = 100;//使用静态可以让四个线程共享变量tick
	//第二步:覆写run方法
	public void run()
	{
		while(true)
		{
			if(tick>0)
				System.out.println(currentThread().getName()+"sale:"+tick--);
		}
	}
}
class  
{
	public static void main(String[] args) 
	{
		//第三步:创建Runnable子类对象
		Ticket t = new Ticket();
		//第四步:建立Thread对象,将Runnable子类对象传递进来作为参数
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		//第五步:调用start()方法
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}
注:a、为什么要将Runnable接口的子类对象传递给Thread的构造函数?

因为,自定义的run方法所属对象是Runnable接口的子类对象,所以要让线程去执行指定对象,就必须明确该run方法所属对象

b、实现接口Runnable的好处(重点):

(1).将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
(2).避免了Java单继承的局限性。所以,创建线程的第二种方式较为常用。


第三部分:多线程的安全问题

1、多线程的运行会出现安全问题,出现问题的原因是:

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

2、解决方案:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程都不能执行。

在Java中对多线程的安全问题提供了专业的解决方案:使用同步代码快。

synchronized(对象)
{
	需要被同步的代码
}
上面的对象参数如同锁,持有锁才可以在同步中执行,没有持有锁的线程即使获得cpu的执行权也无法执行同步的代码。

3、同步的前提:

a、必须要有两个或者两个以上的线程

b、必须是多个线程使用同一个锁。

c、必须保证同步中只有一个线程在执行。

4、同步的好处和弊端:

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

弊端:多个线程都要判断锁,较为消耗资源。

5、代码示例(以刚才售票的例子为例)

	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					try{Thread.sleep(10);}catch (Exception e){}
					System.out.println(currentThread().getName()+"sale:"+tick--);
				}
			}
		}
	}

6、同步的另一种方式:使用同步函数

public synchronized void method()//使用this做锁
{
        需要同步的代码快
}
这时候有一个问题:同步函数的锁是哪一个?
同步函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁是this。

7、同步函数和同步代码块的区别:
a、同步函数的锁是固定的this。
b、同步代码块的锁是任意的对象。建议使用同步代码块。

由于同步函数的锁是固定的this,同步代码块的锁是任意的对象,那么如果同步函数和同步代码块都使用this作为锁,即sysnchronized(this),就可以实现同步。

8、静态同步函数的锁

public static synchronized void method()
        需要同步的代码快
}
静态函数进入内存后,内存中没有本类对象,但是一定有该类对应的字节码对象。

字节码对象:当前类名.class(该类的类型是Class)或者getClass()得到。

因此,静态同步函数使用的锁是该方法的字节码文件对象(类名.class)。


第四部分:使用同步的懒汉式单例设计模式

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;
	}
}

面试时的问题:
a、懒汉式和饿汉式有什么区别
懒汉式是实例的延迟加载
b、懒汉式延迟加载有什么问题
如果多线程会出现安全问题,可以加同步来解决
c、同步的方式
用同步函数或者同步代码快都行,但是有些低效,可以用双重判断来解决低效问题
d、同步时候使用的锁是谁
该类所属的字节码对象
f、请给我写一个延迟加载的单利设计模式
以上代码


注:饿汉式没有安全问题。

Class Single{
	private static Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{		
		return s;
	}
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值