黑马程序员_多线程的理解
2012-08-17 13:21
211 查看
---------------------- android培训、java培训、期待与您交流! ----------------------
1、进程和线程的概念
|--进程:正在运行的程序,也可以说是程序在内存中开辟空间。
|--线程:进程的最小控制单元,用来控制进程的执行,一个进程中至少有一个线程。
2、JVM中的多线程体现
|--主线程、垃圾回收线程、自定义线程以及它们运行的代码位置。
JVM就是多线程的,因为在JVM运行程序的时候会不断的在内存中产生垃圾,这时候垃圾回收线程就会不定期的进行垃圾回收,然而程序的运行是从主函数开始的,这时候有主函数的线程在控制,而且和垃圾回收线程是同时进行,这是两个独立的线程来控制的。
Thread类就是一个线程Thread,直接创建其对象,就是一个线程,run既然是运行方法,那么里面存储了线程要运行的代码,可以想要创建线程运行我们自己制定的代码,那么这时就应该利用继承思想,将Thread类进行继承,并覆盖已有的run方法,定义自己要运行的线程的代码。
3、什么时候使用多线程,多线程的好处是什么?创建线程的目的。
|--当需要多部分代码同时执行时,
|--好处就是让多部分代码同时执行。
创建线程的目的:让单独的一个线程去运行指定的代码。
4、创建线程的的 两种方式?
|--继承Thread类
|--步骤:
1、继承Thread类
2、覆盖run方法,把药运行的代码存储在run方法中。
3、调用thread类的start的方法开启线程,再调用run方法。
|--实现Runnbale
|--步骤
1、实现Runnable接口
2、覆盖Runnable接口的run方法,将线程要运行的代码存储到run方法当中
3、将实现Runnable接口的子类对象作为实际参数传递到Thread类的构造函数中。因为线程对象创建后必须要明确要运行的run方法,而Runnable方法所属的对象是Runnable接口的子类对象。
4、调用thread类的start方法。
|--两种方式的区别
1、实现Runnable接口的避免了单继承局限性。
2、把线程运行的代码单独的封装到Runnable接口类型的对象中,这样实现了线程对象和任务对象的解耦。
5、线程的5种状态
|--被创建:即不具备资格也不具备执行权。
|--运行:在此时只有一个线程在运行,线程具备了执行资格也具备了执行权。
|--冻结:此时线程放弃了执行资格和执行权。
|--消亡:释放路径。
|--临时阻塞:具备执行资格,但是没有执行权。等待CPU切换到它执行。
6、线程的安全问题:
|--安全问题的原因:
1、多线程同时操作共享数据。
2、操作共享数据的语句不止一条。
|--解决的思想:
让一个线程在执行多条操作共享数据的语句时,其他线程不能同时操作共享数据的语句。
|--解决的体现:
同步:
Synchronized()
{
需要被同步的代码块
}
|--同步的前提:当加上同步还出现了安全问题。就需要这个前提来思考。
1、是否是多线程,如果只有一个线程在执行就不必要使用同步。
2、多线程在同步时是否使用的是同一把锁。
|--同步的两种表现方式和区别:
同步代码块:使用的锁是任意对象
同步函数:使用的锁是this
静态同步函数:使用的锁是对象的字节码文件。
|--同步的好处和弊端
有效的解决了线程存在的安全问题,因为当一个线程在操作共同数据时,通过同步锁的机制,使得其他的线程不能去操作共享的数据,不管这条操作语句在执行过程中处于什么状态,消亡除外。比如sleep或者wait,都要等到它重新获取了执行权,把执行语句执行完毕后才出去。这样的话就保证了其他线程在有线程在操作共享数据的时候不能再操作共享数据。
弊端:因为每次都要进行对锁的判断,会降低程序运行的效率和性能。
|--单例的懒汉式:
单例设计模式中的懒汉式又称为延迟加载,当调用到getInstance方法需要被创建对象的 时候才会被创建,由于多线程的并发访问所以存在着安全问题,下面结合代码来分析:
Class Single
{
Private Single(){}
Private static Single s=null;
Public static void getInstance()
{
If(s==null)
{
Synchronized(Single.class)
{
If(s==null)
S=new Single();
}
Reyturn s
}
}
}
|--死锁:
同步嵌套的过程中容易引发死锁,因为使用的的锁不一样。
代码体现:
class Test implements Runnable
{
private boolean flag = true;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
while(true)
{
if(flag)
{
synchronized(MyLock.lock_a)
{
System.out.println(Thread.currentThread().getName()+"..if lock_a");
synchronized(MyLock.lock_b)
{
System.out.println(Thread.currentThread().getName()+"..if lock_b");
}
}
}
else
{
synchronized(MyLock.lock_b)
{
System.out.println(Thread.currentThread().getName()+"..else lock_b");
synchronized(MyLock.lock_a)
{
System.out.println(Thread.currentThread().getName()+"..else lock_a");
}}}}}}
class MyLock
{
public static Object lock_a = new Object();
public static Object lock_b = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Test t1 = new Test(true);
Test t2 = new Test(false);
new Thread(t1).start();
new Thread(t2).start();
}}
7、线程间通信,等待/唤醒机制
|--概念:
其实就多个线程在操作同一个资源,但是操作的动作不同,动作不同,代表着线程的任务不一样,就意味着要对任务对象进行单独的封装和描述。
|--等待唤醒机制:使用了wait notify notifyALL 线程池的概念:
Wait();让当前线程出于冻结状态,当前线程被存储到线程池当中。
Notify();唤醒线程池中任意一个线程,让其恢复到运行状态,具备CPU的执行资格。
notifyAll();唤醒线程池中所有的线程,让它们都具备执行资格。
|--生产者和消费者的问题、并多生产和多消费的问题。While判断标记 notifyAll唤醒对象
问题:
1、出现了错误数据,是因为多生产多消费的时候,被唤醒的线程没有判断标记就执行了,解决问题的方法将if改变成while();
2、有了while()循环后,出现了死锁,因为本方线程唤醒的线程还有可能是本方线程,所以导致死锁,解决方法:本方必须唤醒对方才可以,而notify只能唤醒一个,所以把线程池中所有的线程都唤醒用notifyAll。但是这样做的效率较低,所以出现了后面的更好的解决方案。
|--JDK1.5以后出现的更好的方案:
Lock接口替代了synchronized
隐式锁机制和显示锁机制的区别:
Public void run()
{
Synchronized(obj)
code...
}
这种方式为隐式锁机制,我们不能清楚的知道它里面是如何获取锁和释放锁的。
Lock.lock
Public void run()
{
Try
{
Lock.lock();获取锁
Code..
}
Finally这里也是finally的用法的体现,一定要执行的代码,常常用于关闭资源
{
Lock.unlock();释放锁,
}
}
Condition接口替代了Object中的监视器方法,并将监视器方法封装成了condition和以前不同的是,以前一个锁上只能有一组监视器方法,现在一个lock锁上可以多组监视器方法对象,可以实现一组负责生产者,一组负责消费者。
代码实现:
private Lock lock = new ReentrantLock();//用reentrantLock创建一个lock对象。
private Condition con1 = lock.newCondition();//一组监视器监视生产者
private Condition con2 = lock.newCondition();//一组监视器监视生产者
|--wait和sleep区别:
1、sleep必须要指定时间,wait可以指定时间,也可以不指定时间。
2、Wait必须同在同步当中,sleep不一定。
3、二者在处理线程的状态上有所不同:
Sleep是放弃执行权,没有释放锁。
Wait是执行权和锁全部都放弃。
8、停止线程的方式。
|--原理:stop方法已经过时,因为该方法具有不确定的安全性,那么结束线程只有一种方法:就是执行线程的代码结束,线程会自动终止。
1、run方法中通常有循环,只要控制循环结束,所以要控制循环条件,最常用的方法就是定义标记。
2、如果run方法中有同步,可以让线程出于冻结状态,比如遇到wait()时,那么线程就不会去读取标记,这时候必须让线程恢复到运行状态,才有可能去读取标记,所以通过正常方法notify、sleep时间到但是如果没有正常方法恢复,就必须使用强制手段,interrupt()强制将线程冻结状态清楚,让线程恢复运行,这个动作会产生异常情况,要进行处理。
|--表现:定义标记,问题?容易读不到,使用中断
9、线程常见一些方法
|--setDaemon();守护线程:当正在运行的线程是守护线程时,JVM会自动退出,该方法开启之前调用,
扩展:线程分两种:
前台线程:我们自己定义的线程都可以称之为前台线程
后台线程:一旦调用了此方法为守护线程之后,都是后台线程。
区别:如果线程结束了,守护线程也随着结束,
|--join():A线程加入到B线程时,这时候B线程就会等待,等到A线程运行完为止,与其他线程无关。把执行权给了谁,就只和给的对象的执行权有关,等它结束之后,就会重新获取执行
|--优先级:代表抢夺资源的频率。有两种方法:getPriority setPriority来获取优先级和设置优先级,只有十个数字可以使用1-10这十个数字,同时线程中提供了几个特殊的方法:
线程类中提供了MAX_PRIORITY MIN_PRIORTY NORM_PRIORTY。
|--yield():结束该线程,释放执行权。
|--在开发时,可以使用匿名内部类来完成局部路径的开辟。
---------------------- android培训、java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net/heima
1、进程和线程的概念
|--进程:正在运行的程序,也可以说是程序在内存中开辟空间。
|--线程:进程的最小控制单元,用来控制进程的执行,一个进程中至少有一个线程。
2、JVM中的多线程体现
|--主线程、垃圾回收线程、自定义线程以及它们运行的代码位置。
JVM就是多线程的,因为在JVM运行程序的时候会不断的在内存中产生垃圾,这时候垃圾回收线程就会不定期的进行垃圾回收,然而程序的运行是从主函数开始的,这时候有主函数的线程在控制,而且和垃圾回收线程是同时进行,这是两个独立的线程来控制的。
Thread类就是一个线程Thread,直接创建其对象,就是一个线程,run既然是运行方法,那么里面存储了线程要运行的代码,可以想要创建线程运行我们自己制定的代码,那么这时就应该利用继承思想,将Thread类进行继承,并覆盖已有的run方法,定义自己要运行的线程的代码。
3、什么时候使用多线程,多线程的好处是什么?创建线程的目的。
|--当需要多部分代码同时执行时,
|--好处就是让多部分代码同时执行。
创建线程的目的:让单独的一个线程去运行指定的代码。
4、创建线程的的 两种方式?
|--继承Thread类
|--步骤:
1、继承Thread类
2、覆盖run方法,把药运行的代码存储在run方法中。
3、调用thread类的start的方法开启线程,再调用run方法。
|--实现Runnbale
|--步骤
1、实现Runnable接口
2、覆盖Runnable接口的run方法,将线程要运行的代码存储到run方法当中
3、将实现Runnable接口的子类对象作为实际参数传递到Thread类的构造函数中。因为线程对象创建后必须要明确要运行的run方法,而Runnable方法所属的对象是Runnable接口的子类对象。
4、调用thread类的start方法。
|--两种方式的区别
1、实现Runnable接口的避免了单继承局限性。
2、把线程运行的代码单独的封装到Runnable接口类型的对象中,这样实现了线程对象和任务对象的解耦。
5、线程的5种状态
|--被创建:即不具备资格也不具备执行权。
|--运行:在此时只有一个线程在运行,线程具备了执行资格也具备了执行权。
|--冻结:此时线程放弃了执行资格和执行权。
|--消亡:释放路径。
|--临时阻塞:具备执行资格,但是没有执行权。等待CPU切换到它执行。
6、线程的安全问题:
|--安全问题的原因:
1、多线程同时操作共享数据。
2、操作共享数据的语句不止一条。
|--解决的思想:
让一个线程在执行多条操作共享数据的语句时,其他线程不能同时操作共享数据的语句。
|--解决的体现:
同步:
Synchronized()
{
需要被同步的代码块
}
|--同步的前提:当加上同步还出现了安全问题。就需要这个前提来思考。
1、是否是多线程,如果只有一个线程在执行就不必要使用同步。
2、多线程在同步时是否使用的是同一把锁。
|--同步的两种表现方式和区别:
同步代码块:使用的锁是任意对象
同步函数:使用的锁是this
静态同步函数:使用的锁是对象的字节码文件。
|--同步的好处和弊端
有效的解决了线程存在的安全问题,因为当一个线程在操作共同数据时,通过同步锁的机制,使得其他的线程不能去操作共享的数据,不管这条操作语句在执行过程中处于什么状态,消亡除外。比如sleep或者wait,都要等到它重新获取了执行权,把执行语句执行完毕后才出去。这样的话就保证了其他线程在有线程在操作共享数据的时候不能再操作共享数据。
弊端:因为每次都要进行对锁的判断,会降低程序运行的效率和性能。
|--单例的懒汉式:
单例设计模式中的懒汉式又称为延迟加载,当调用到getInstance方法需要被创建对象的 时候才会被创建,由于多线程的并发访问所以存在着安全问题,下面结合代码来分析:
Class Single
{
Private Single(){}
Private static Single s=null;
Public static void getInstance()
{
If(s==null)
{
Synchronized(Single.class)
{
If(s==null)
S=new Single();
}
Reyturn s
}
}
}
|--死锁:
同步嵌套的过程中容易引发死锁,因为使用的的锁不一样。
代码体现:
class Test implements Runnable
{
private boolean flag = true;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
while(true)
{
if(flag)
{
synchronized(MyLock.lock_a)
{
System.out.println(Thread.currentThread().getName()+"..if lock_a");
synchronized(MyLock.lock_b)
{
System.out.println(Thread.currentThread().getName()+"..if lock_b");
}
}
}
else
{
synchronized(MyLock.lock_b)
{
System.out.println(Thread.currentThread().getName()+"..else lock_b");
synchronized(MyLock.lock_a)
{
System.out.println(Thread.currentThread().getName()+"..else lock_a");
}}}}}}
class MyLock
{
public static Object lock_a = new Object();
public static Object lock_b = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Test t1 = new Test(true);
Test t2 = new Test(false);
new Thread(t1).start();
new Thread(t2).start();
}}
7、线程间通信,等待/唤醒机制
|--概念:
其实就多个线程在操作同一个资源,但是操作的动作不同,动作不同,代表着线程的任务不一样,就意味着要对任务对象进行单独的封装和描述。
|--等待唤醒机制:使用了wait notify notifyALL 线程池的概念:
Wait();让当前线程出于冻结状态,当前线程被存储到线程池当中。
Notify();唤醒线程池中任意一个线程,让其恢复到运行状态,具备CPU的执行资格。
notifyAll();唤醒线程池中所有的线程,让它们都具备执行资格。
|--生产者和消费者的问题、并多生产和多消费的问题。While判断标记 notifyAll唤醒对象
问题:
1、出现了错误数据,是因为多生产多消费的时候,被唤醒的线程没有判断标记就执行了,解决问题的方法将if改变成while();
2、有了while()循环后,出现了死锁,因为本方线程唤醒的线程还有可能是本方线程,所以导致死锁,解决方法:本方必须唤醒对方才可以,而notify只能唤醒一个,所以把线程池中所有的线程都唤醒用notifyAll。但是这样做的效率较低,所以出现了后面的更好的解决方案。
|--JDK1.5以后出现的更好的方案:
Lock接口替代了synchronized
隐式锁机制和显示锁机制的区别:
Public void run()
{
Synchronized(obj)
code...
}
这种方式为隐式锁机制,我们不能清楚的知道它里面是如何获取锁和释放锁的。
Lock.lock
Public void run()
{
Try
{
Lock.lock();获取锁
Code..
}
Finally这里也是finally的用法的体现,一定要执行的代码,常常用于关闭资源
{
Lock.unlock();释放锁,
}
}
Condition接口替代了Object中的监视器方法,并将监视器方法封装成了condition和以前不同的是,以前一个锁上只能有一组监视器方法,现在一个lock锁上可以多组监视器方法对象,可以实现一组负责生产者,一组负责消费者。
代码实现:
private Lock lock = new ReentrantLock();//用reentrantLock创建一个lock对象。
private Condition con1 = lock.newCondition();//一组监视器监视生产者
private Condition con2 = lock.newCondition();//一组监视器监视生产者
|--wait和sleep区别:
1、sleep必须要指定时间,wait可以指定时间,也可以不指定时间。
2、Wait必须同在同步当中,sleep不一定。
3、二者在处理线程的状态上有所不同:
Sleep是放弃执行权,没有释放锁。
Wait是执行权和锁全部都放弃。
8、停止线程的方式。
|--原理:stop方法已经过时,因为该方法具有不确定的安全性,那么结束线程只有一种方法:就是执行线程的代码结束,线程会自动终止。
1、run方法中通常有循环,只要控制循环结束,所以要控制循环条件,最常用的方法就是定义标记。
2、如果run方法中有同步,可以让线程出于冻结状态,比如遇到wait()时,那么线程就不会去读取标记,这时候必须让线程恢复到运行状态,才有可能去读取标记,所以通过正常方法notify、sleep时间到但是如果没有正常方法恢复,就必须使用强制手段,interrupt()强制将线程冻结状态清楚,让线程恢复运行,这个动作会产生异常情况,要进行处理。
|--表现:定义标记,问题?容易读不到,使用中断
9、线程常见一些方法
|--setDaemon();守护线程:当正在运行的线程是守护线程时,JVM会自动退出,该方法开启之前调用,
扩展:线程分两种:
前台线程:我们自己定义的线程都可以称之为前台线程
后台线程:一旦调用了此方法为守护线程之后,都是后台线程。
区别:如果线程结束了,守护线程也随着结束,
|--join():A线程加入到B线程时,这时候B线程就会等待,等到A线程运行完为止,与其他线程无关。把执行权给了谁,就只和给的对象的执行权有关,等它结束之后,就会重新获取执行
|--优先级:代表抢夺资源的频率。有两种方法:getPriority setPriority来获取优先级和设置优先级,只有十个数字可以使用1-10这十个数字,同时线程中提供了几个特殊的方法:
线程类中提供了MAX_PRIORITY MIN_PRIORTY NORM_PRIORTY。
|--yield():结束该线程,释放执行权。
|--在开发时,可以使用匿名内部类来完成局部路径的开辟。
---------------------- android培训、java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net/heima
相关文章推荐
- 黑马程序员-多线程(线程的安全问题与锁的理解)
- 黑马程序员 多线程的理解
- 黑马程序员--07.集合框架--并发访问异常理解:一个单线程程序的多线程运行思想【个人总结】
- 黑马程序员_多线程的简单理解
- 黑马程序员--java多线程的理解
- 黑马程序员——Java基础---理解多线程
- 黑马程序员——多线程的理解
- 黑马程序员_多线程的理解与使用
- 深入理解JVM-类加载和多线程
- 黑马程序员——JAVA基础----多线程(一)
- 对于多线程的理解
- 黑马程序员——Java基础->多线程
- 对于多线程两种方式的理解
- 黑马程序员0831_java基础知识+多线程部分
- 黑马程序员 课堂笔记-心得 多线程及同步锁
- 黑马程序员—java基础_多线程
- 黑马程序员--多线程
- 黑马程序员 个人理解
- 黑马程序员_多线程
- 深入理解多线程