------- android培训、java培训、期待与您交流! ----------
1.线程和进程的概念
1.1.进程(Process):拥有独立的内存空间,每个独立执行的程序称为进程
1.2.线程(Thread):线程是一个程序内部的一条执行路径,Java虚拟机允许应用程序并发地运行多个执行线程
1.3.线程和进程的区别
每个进程都有独立的代码和数据空间(进程上下文),进程间的切换开销大
线程: 同一进程内的线程共享代码和数据空间,线程切换的开销小
多进程: 在操作系统中能同时运行多个任务(程序)
多线程: 在同一应用程序中多条执行路径同时执行
2.线程的创建与启动
第一种:继承Thread类,重写run方法
第二种:实现Runnable接口,重写run方法
3.两种创建方式的比较
3.1使用Runnable接口
还可以从其他类继承;
保持程序风格的一致性。
3.2直接继承Thread类
不能再从其他类继承;
编写简单,可以直接操纵线程
使用实现Runnable接口的方式创建线程时可以为相同程序代码的多个线程提供共享的数据。
4.线程小结
4.1.Java的线程是通过java.lang.Thread类来实现的。
4.2.当程序启动运行时,JVM会产生一个线程(主线程),主方法(main方法)就是在这个线程上运行的。
4.3.可以通过创建Thread的实例来创建新的线程。
a.每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。
b.通过调用Thread类的start()方法来启动一个线程。线程进入Runnable(可运行)状态,它将向线程
调度器注册这个线程。
c.调用start()方法并不一定马上会执行这个线程,正如上面所说,它只是进入Runnable 而不是
Running。
4.4.每个线程都有一个优先级,一个标识名,多个线程可以同步。没有指定标识名时,会自动为其生成一个
高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程
中运行的代码创建一个新 Thread
对象时,该新线程的初始优先级被设定为创建线程的优先级,并且
当且仅当创建线程是守护线程时,新线程才是守护程序。
4.5.Java虚拟机退出的情况
- 调用了
Runtime
类的exit
方法,并且安全管理器允许退出操作发生。 - 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到
run
方法之外的异常
注意,不要直接在程序中调用线程的run()方法。
5.线程的基本使用方法
- static Thread currentThread ();//返回当前正在执行的线程对象的引用
- static void yield ();//暂停当前线程一下下。
- static void sleep (long millis);//线程睡眠(暂停执行)
- 上面三个都是静态方法,其他的一般不是
- void start();//使线程启动,进入runnable状态。
- boolean isAlive();//判断该线程是否处于活动状态
- boolean isDaemon();//判断是否是守护线程
- Thread.State getState();//返回该线程的状态
- void setName(String name);//改变线程名称
- void setDaemon(boolean on);//true则为守护进程,false为用户进程。
6.线程的调度
- 线程睡眠: Thread.sleep(long miao);//使线程进入阻塞状态,睡眠结束后进入就绪(Runnable)状态
- 线程让步: Thread.yield();//暂停当前执行线程,将执行机会让给同等或更高优先级的线程执行,但是这个时间不能控制.是不确定的.只是让当前线程暂停一下下.
- 线程加入: join();//throws InterruptedException 。当前线程调用另一个线程的join方法,则当前线程
转入waiting状态,直到调用join()方法的线程执行完毕,当前线程再由阻塞转为就绪状态。
4.线程等待:Object类中的wait()方法,导致当前线程等待,直到其他线程调用此对象的notify()方法或notifyAll
()方法唤醒该线程。
5.线程唤醒:Object类的notify()方法,唤醒在。此对象监视器上等待的某个单个线程。选择是随意的。、
Object类的notifyAll()方法,唤醒在此对象监视器上等待的所有线程。
注意:这三个方法(wait,notify,notifyAll)只能在被同步化(synchronized)的方法或代码块中使用。
7.线程停止
- 如果run方法中执行的是一个重复执行的循环,可以提供一个标志来控制循环是否执行。
- 如果线程是因为执行了sleep()方法或wait()方法而进入了阻塞状态,此时想停止它的话可以使用interrupt()方法
程序会抛出InterruptException异常。
- 如果程序因为输入/输出的等待而阻塞,基本上是必须等待输入/输出的动作完成才能离开阻塞状态。无法通过interrupt
方法使得线程离开run方法,要想离开只能引发一个异常。
7.1 线程的终止
- 因为run方法的正常退出而自然死亡
- 因为一个没有捕获的异常终止了run方法而意外死亡
8.线程状态的转换
创建(new)---->[阻塞解除]----->就绪(runnable)---->运行(running)--->[阻塞]---->死亡(dead)
新建状态(new):新创建一个线程对象。
就绪状态(runnable):其他线程调用了该线程的start方法后,该状态的线程位于可运行线程池中,变得可运行,等待
获取cpu使用权。获取了cpu,执行程序代码时也属于就绪状态。
运行状态(running):执行run方法。
阻塞状态(blocked):阻塞状态是因为线程由于某个原因而放弃cpu的使用权,而暂时停止运行,直到线程进入就绪状态
才有机会进入运行状态。阻塞情况有三种:
1.同步阻塞:运行的线程在获取对象的同步锁时,如果该同步锁被其他线程占用,则jvm将它放入锁池中
2.等待阻塞:运行的线程执行wait的方法时,jvm会把该线程放入等待池中。
3.其他阻塞:运行的线程执行sleep()方法或join()方法,或发出I/O请求时,将进入阻塞状态
死亡状态(dead):线程执行完或因异常退出run方法,线程结束生命周期。
注意:调用yield()方法并没有进入阻塞(blocked)状态
9.调整线程的优先级
Java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程。优先级高的线程会获得较多的运行机会
Java线程的优先级用int表示,取值1~10,Thread类的三个静态常量:MAX_PRIORITY=10,MIN_PRIORITY=1
NORM_PRIORITY=5(默认优先级取值为5).
Thread类的setPriority(),getPriority()方法可以设置和获取线程的优先级
注意:优先级高的只是得到cpu的机会概率多一些
10.线程同步(同步锁)
在Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性,每个对象都对应于一个可称为"互斥锁"
的标记,这个标记保证在任何时刻,只能有一个线程访问对象。
关键字synchronized用来与对象互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任何时刻
只能由一个线程访问。
注意:同步锁即synchronized(参数)里的参数必须是引用类型,如成员变量(必须是引用类类型:String)
成员方法(方法的返回值必须是引用类型,不能是int,char),this(即本身对象),this.getClass()//类
11.Synchronized关键字
同步代码块: synchronized(this){需要同步的代码;}
同步方法:public synchronized void method(){...}
12.死锁问题
有两个线程,x和y,x线程拿到了A对象锁后执行代码,y线程拿到了B对象锁后执行代码,如果这时x线程还要取
B对象锁,而y线程也要取A对象锁,则大家拿着各自的锁不放,就会导致死锁的问题。
解决死锁的办法之一就是:加大锁的粒度。
13.多线程安全问题
当run()方法中的代码操作到了成员变量(共享数据)时,就会出现多线程安全问题(线程不同步问题)
编程技巧:在方法中尽量少操作成员变量,多使用局部变量。
14.容器类的线程安全
大多数的容器类默认都没有考虑线程安全问题,程序必须自行实现同步:
a.用synchronized来锁住这个对象
synchronized (list){ list.add(...) ;}
b.用Collections类的synchronizedXXX()方法返回一个同步化的容器对象
List list=Collections.synchronizedList(new ArrayList());
这种方式在迭代时仍要用synchronized修饰。
List list = Collections.synchronizedList( ArrayList()); ... (list) { Iterator i = list.iterator(); (i.hasNext()) {foo(i.next());}}
JDK 5.0之后,新增了java.util.concurrent这个包,其中包括了一些确保线程安全的容器类,
如ConcurrentHashMap,CopyOnWriteArrayList,CopyOnWriteArraySet,它们在效率
与安全性上取得了较好的平衡。
总结:
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
1.调用run和调用start有什么区别
run方法只是类里的一个普通方法,调用该方法只是执行该方法里的代码,程序中还是只有主线程这一线程
而start方法是将该线程变成可运行状态(还没运行),等待cpu切换来时就可以启动执行该线程下的run方法。
即start方法可以启动线程,而run方法只是Thread的一个普通方法。
2.静态的同步方法与其他同步代码块之间的同步
静态同步方法:public static synchronized void method(){...};
//它的同步锁是该方法所属字节码文件对象即 类名.getClass()或 类名.class
所以与它同步的代码块里必须是:synchronized(类名.class) {.....}
非静态的方法的同步锁是this对象本身
3.总结
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。
4.wait 和 sleep 区别?
wait可以指定时间也可以不指定。sleep必须指定时间。
在同步中时,对cpu的执行权和锁的处理不同。 wait:释放执行权,释放锁。 sleep:释放执行权,不释放锁。
5.wait,notify,notifyAll方法使用
这三个方法经常用在线程间的通信,比如:X线程对某个数据进行一次更改后,Y线程对其读取一次,是一次更改一次读取交替进行,
不是更改多次后再读取多次。它们只能在同步方法或同步代码块中调用,且同步锁必须为这三个方法的共同对象。无论线程调用一个
对象的wait还是notify方法,该线程必须先得到该对象的锁,才可以调用该方法。notify只能唤醒同一个对象监视器中调用wait的线程。
以下是,生产者生产(更改)一次后,换消费者消费(读取)一次。依次交替。
6.主线程
Java VM 启动的时候会有一个进程java.exe.
该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
------- android培训、java培训、期待与您交流! ----------