码迷,mamicode.com
首页 > 编程语言 > 详细

黑马程序员_多线程总结

时间:2015-04-18 20:21:10      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:


/**
多线程概述:什么是多线程?
首先,什么是进程?
进程是系统分配资源的基本单位,对于windows系统而言。
进程是程序的一次运行。
一个进程中至少有一个线程存在,因为线程才是实际运行单元。
线程:是指程序中的控制单元,一条执行路径。
一个程序可以有多线程并发执行,每个线程是一个控制单元。一个程序可以有多条执行线路,
每个执行线路代表一个线程。
线程的创建是由底层系统完成的,对于Java中的线程是jvm通过调用底层的windows操作系统的功能创建的。

注意一个线程在程序上的体现和内存中体现就是一个对象,一个线程对象。用一个Thread类或者其子类可以创建
很多个不同的线程对象,即很多个不同的线程。

线程的创建:线程的创建方式有两种:
1、通过继承Thread类,覆写其中的run方法。
2、通过实现Runnable接口,覆写其中的run方法。

线程是一类事物,是指程序的一个控制单元。Java中有对于此类的描述,叫做Thread类,该类中的run方法,即
public void run(){};,这个空参数的内部什么操作也没有的方法是供Thread的子类覆写其,并在run中存放
该线程要执行的代码。
Thread类的一些方法:
public void run();
public String getName();//返回该线程的名称。
public String setName(String name);//设置该线程的名称。
public static Thread currentThread();//返回对当前正在执行的线程对象的引用。
public static void sleep(long millis,int nanos);//使得执行该方法的线程进入到阻塞状态,开始休眠。
第一种创建方式:
1、通过继承Thread类。
2、覆写其中的run方法。
3、将该线程需要运行的代码存放到run()方法中
4、创建子类对象
5、调用子类对象的start()方法,开启线程,并调用run方法。

第二种创建方式:
1、通过实现Runnable接口,定义类。
2、覆写其中的run方法。
3、将需要线程运行的代码存放到run()方法中
4、创建自定义类的对象。
5、创建Thread类的对象,并将自定义类的对象作为参数传递给Thread类的带参数的构造函数。创建出一个Thread类的
对象。
6、调用Thread类对象的start()方法,开启线程,并调用自定义类对象的run方法。

两种方式的区别比较:
区别:
第二种方式,实现Runnable接口的类并不是一个线程类,创建该接口的子类对象,并不是创建了线程对象。
第二种方式是通过创建Thread类的对象,并指定所要运行的run方法位于哪个对象中,来创建线程对象的。

优缺点:第一种方式,由于Java的单继承,使得Thread子类不能在继承其他的类,无法成为其他类的一种。有一定的
局限性。而第二种方式,通过实现Runnable接口的方式,避免了继承Thread的,所以是避免了单继承的局限性。
在实际开发中尽可能使用第二种方式,第二种方式是将Runnable接口所定义的功能扩展到了接口子类上。
凡是实现了Runnable接口的类的对象,都可以交给Thread类的对象来运行其中特定的内容。

线程的运行状态:
一个线程的运行状态有5种:是一个类似于生物一样的声明周期。
分别是新建状态、就绪状态、运行状态、阻塞状态、死亡状态。具体如下:

1、新建(new): 用new语句创建完一个线程,该线程处于新建状态,此时它和其他java对象一样,仅仅在Heap中被分配了内存。
当一个线程处于新建状态时,它仅仅是一个空的线程对象,系统不为它分配资源。
例如:Thread t = new Thread(new Runner());
2、就绪(runnable): 程序通过线程对象调用启动方法start()后,系统会为这个线程分配它运行时所需的除处理器cpu之外的
所有系统资源。这时,它处在随时可以运行的状态,在随后的任意时刻,只要它获得处理器即会进入运行状态(running)。
例如:t.start();
3、运行(running): 处于这个状态的线程占用cpu,执行程序代码。在并发环境中,如果计算机只有一个cpu,那么任何时刻
只会有一个线程处于这个状态。如果计算机中有多个cpu(即多核),那么同一时刻可以让几个线程占用不同的cpu,使它们都处
于运行状态,只有处于就绪状态(runnable)的线程才有机会转到运行状态。其他线程状态不可以直接切换到运行状态,
必须先进入就绪状态才可以。
4、阻塞(blocked): 阻塞状态是指线程因为某些原因放弃cpu,暂时停止运行。当线程处于阻塞状态时,java虚拟机不会给
线程分配cpu,直到线程重新进入就绪状态,它才有机会转到运行状态。
阻塞状态可以分为以下3中:
@1,位于对象等待池中的阻塞状态(blocked in object‘s wait pool):当线程处于运行状态时,如果
执行了某个对象的wait()方法,java虚拟机就会把线程放到这个对象的等待池中。
@2,位于对象锁池中的阻塞状态(blocked in object‘s lock pool):当线程处于运行状态,
试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,JVM就会把这个线程放到这个
对象的锁池中。
@3,其他阻塞状态(otherwise blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,
或者发出了I/O请求时,就会进入这个状态。当一个线程执行System.out.println()或者System.in.read()方法时,
就会发出一个I/O请求,该线程放弃cpu,进入阻塞状态,直到I/O处理完毕,该线程才会恢复执行。
5、死亡(dead): 当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。线程有可能是正常执行完run()方法而
退出,也有可能是遇到异常而退出。不管线程是正常结束还是异常结束,都不会对其他线程造成影响。

线程状态之间的切换,新建状态只可以进入就绪,就绪可以到运行,运行也可以到就绪,运行可以到阻塞,阻塞可以到
就绪,新建、就绪、运行、阻塞都可以到死亡。

多线程的安全问题:当多个线程执行同一块代码,操作共享数据的时候,就会出现线程安全问题。

线程安全问题出现的前提:
1、至少有两个或者两个以上的线程存在。
2、至少有两条或者两条以上的代码操作共享资源。

解决方法:让线程同步。
什么事线程同步?什么是线程异步?
线程具有随机性或者说异步性,即每个线程都以各自独立的、不可预知的速度向前推进。即每个线程各自执行各自
的,互不相干。
同步是指:因为需要互斥的使用某一个共享资源,或者需要协同合作而产生了相互制约关系。即线程的推进执行中
需要考虑其他线程的执行情况了。
解决线程安全的问题,是使用同步代码块或者同步函数。关键字为synchronized。

同步代码块:
synchronized(对象)
{
需要同步的代码块
}
同步函数:
synchronized 返回值类型 函数名()
{
//函数体,需要同步的代码。
}
同步的部分一次只能有一个线程进入运行,同步的部分都有一个锁,无论是同步代码还是同步函数,这个锁是一个对象。
对于同步代码块就是指synchronize(对象)中的对象。而对于同步函数,如果是非静态的同步函数,则是调用该
非静态同步函数的对象,即this;如果是静态的同步代码块,则是该静态同步函数所属的类的类对象,即该类的
字节码文件对象,即类名.class,“类名.class”就是这个对象的名称,该对象的类型是Class类型。
同步部分的进入规则是,只有拿到锁的线程才可以进入同步部分执行,执行完同步部分后,再将该锁释放,没有拿到该
锁的线程无法进入同步部分,只能是在外等候,因为缺少资源,放弃CPU,进入阻塞状态。

同步:
好处:解决了线程安全问题
弊端:因为每次进入同步部分,都需要对同步部分进行锁判定,降低了程序的效率。但有时这种牺牲是必要的。

死锁:死锁是指由于多个线程之间占有对方需要的锁,而又请求对方占有的锁,造成的一个无法解开的局面,死锁。
死锁产生的原因:同步中嵌套同步。
例1:
synchronized(lockA)
{
synchronzied(lockB)
}
synchronized(lockB)
{
synchronized(lockA)
}
例2:
sychronized void show()
{
method();
}
sychronized void method()
{
show();
}
单例模式中的懒汉式,即延迟加载的方式,当有多线程访问时,会出现线程安全问题。解决方法是加同步代码块或者同步函数。
但是加入同步后,会降低程序的效率,因为每个线程在进入同步部分时,都要进行同步锁的判断。而优化的方案是,使用
双重判断,来提高程序的效率。
*/

class Demo extends Thread
{
	Demo(String name)
	{
		super(name);
	}
	public void run()
	{
		for(int x=0; x<60; x++)
		{
			System.out.println(Thread.currentThread().getName()+"---run----"+x);
		}
	}
}

class  ThreadDemo
{
	public static void main(String[] args) 
	{
		Demo d = new Demo("Demo");
//		System.out.println(d.getName());

//		d.run();

		d.start();
		for(int x =0 ;x<60; x++)
		{
			System.out.println(Thread.currentThread().getName()+"---main---"+x);
		}
		
	}
}

  

 

/*
主函数也是一个线程,它的名称叫做main,一般称为主线程。
而其他线程的名称可以在创建时就赋值,如果使用默认名称,则为Thread-编号。
run()和start()的区别:
run方法只是用来存储线程需要运行的代码。
start()方法是用来开启线程的,只有开启线程,系统才会给该线程分配除cpu以外的资源。然后会调用run方法。
*/

黑马程序员_多线程总结

标签:

原文地址:http://www.cnblogs.com/wllbelief-win/p/4437890.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!