------- android培训、java培训、期待与您交流! ---------
进程和线程
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
进程的特点:每一个进程都有自己的独立的一块内存空间、一组资源系统。其内部数据和状态都是完全独立的。
怎么看待进程?进程的优点是提高CPU运行效率,在同一时间内执行多个程序,即并发执行。但是从严格上讲,也不是绝对的同一时刻执行多个程序,只不过CPU在执行时通过时间片等调度算法不同进程高速切换。
线程:就是进程中的一个独立的控制单元。(一个线程是进程的一个顺序执行流。)
线程在控制着进程的执行。
线程的特点:同类的多个线程共享一块内存空间和一组系统资源。
一个进程中至少有一个线程。
如何在自定义的代码中,自定义一个线程呢?
创建线程的 第一种方式:继承Thread类。
步骤:
1.定义类继承Thread。
2.复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3.调用线程的start方法,
该方法两个作用:启动线程,调用run方法。
class Demo1 extends Thread
{
public void run()
{
System.out.println(Thread.currentThread().getName());
}
}
public class ThreadDemo
{
public static void main(String[] args)
{
Demo1 d = new Demo1();//创建好一个线程。
d.start();//开启线程并执行该线程的run方法。
System.out.println("*************************");
d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
}
}
//--------结果------------
*************************
main
Thread-0
//main说明d.run()仅仅是对象调用方法。而线程创建了,并没有运行。
创建线程的 第二种方式:实现Runnable
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3.通过Thread类建立线程对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
class Ext implements Runnable{
public void run() {
System.out.println("cwk");
}
}
public class RunnableDemo {
public static void main(String[] args) {
Ext ext = new Ext();
Thread t = new Thread(ext);
t.start();
System.out.println("*********************");
new Thread(new Ext()).start();
}
}
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。
在定义线程时,建立使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。
发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
线程五状态:创建、运行、阻塞、挂起、死亡(java编程思想中4状态 创建、运行、阻塞、死亡)
1.创建状态(new Thread())2.运行状态(start())
3.阻塞状态(pend)
(1)通过调用sleep()方法使线程进入休眠状态,线程在指定时间内不会运行。
(2)通过调用join()方法使线程挂起,使自己等待另一个线程的结果,直到另一个线程执行完毕为止。
4.挂起状态(suspend)
挂起实际上是让线程进入“非可执行”状态下,在这个状态下CPU不会分给线程时间片,进入这个状态可以用来暂停一个线程的运行;在线程挂起后,可以通过重新唤醒线程来使之恢复运行。
(1)通过调用wait()方法使线程挂起,直到线程得到了notify()和notifyAll()消息,线程才会进入“可执行”状态。
(2)使用suspend挂起线程后,可以通过resume方法唤醒线程。(不使用)
虽然suspend和resume可以很方便地使线程挂起和唤醒,但由于使用这两个方法可能会造成死锁,因此,这两个方法被标识为 deprecated(抗议)标记,这表明在以后的jdk版本中这两个方法可能被删除,所以尽量不要使用这两个方法来操作线程。
调用sleep()、yield()、suspend()的时候并没有被释放锁
调用wait()的时候释放当前对象的锁
wait()方法表示,放弃当前对资源的占有权,一直等到有线程通知,才会运行后面的代码。
notify()方法表示,当前的线程已经放弃对资源的占有,通知等待的线程来获得对资源的占有权,但是只有一个线程能够从wait状态中恢复,然后继续运行wait()后面的语句。
notifyAll()方法表示,当前的线程已经放弃对资源的占有,通知所有的等待线程从wait()方法后的语句开始运行。
阻塞与挂起的区别:(以前没这个概念,网上引用)
理解一:挂起是一种主动行为,因此恢复也应该要主动完成,而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他什么时候被阻塞(pend),也就不能确切 的知道他什么时候恢复阻塞。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列。
理解二:阻塞(pend)就是任务释放CPU,其他任务可以运行,一般在等待某种资源或信号量的时候出现。挂起(suspend)不释放CPU,如果任务优先级高就永远轮不到其他任务运行,一般挂起用于程序调试中的条件中断,当出现某个条件的情况下挂起,然后进行单步调试。
理解三:pend是task主动去等一个事件,或消息.suspend是直接悬挂task,以后这个task和你没任何关系,任何task间的通信或者同步都和这个suspended task没任何关系了,除非你resume task;
理解四:任务调度是操作系统来实现的,任务调度时,直接忽略挂起状态的任务,但是会顾及处于pend下的任务,当pend下的任务等待的资源就绪后,就可以转为ready了。ready只需要等待CPU时间,当然,任务调度也占用开销,但是不大,可以忽略。可以这样理解,只要是挂起状态,操作系统就不在管理这个任务了。
理解五:挂起是主动的,一般需要用挂起函数进行操作,若没有resume的动作,则此任务一直不会ready。而阻塞是因为资源被其他任务抢占而处于休眠态。两者的表现方式都是从就绪态里“清掉”,即对应标志位清零,只不过实现方式不一样。
5.死亡状态
多线程问题
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问。
//用synchronized进行加锁
class Ticket implements Runnable
{
private int tick = 200;
public synchronized void show()//this
{
try{Thread.sleep(30);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
public void run() {
for(int i = 0; i < 100; i++)
show();
}
}
public class ThisLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}
//同一对象用this进行加锁
class Ticket implements Runnable
{
private int tick = 200;
//private Object obj = new Object();
public void show()
{
try{Thread.sleep(30);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
public void run() {
//synchronized (obj) {
synchronized (this) {
for(int i = 0; i < 100; i++)
show();
}
}
}
//静态加锁的是类本身 Ticket.class
class Ticket implements Runnable
{
private static int tick = 200;
//private Object obj = new Object();
public static synchronized void show()//静态加锁的是类本身Ticket.class
{
try{Thread.sleep(30);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
public void run() {
//synchronized(Ticket.class){}
for(int i = 0; i < 100; i++)
show();
}
}
//死锁(一个先给钱,才能取货;另一个你先给货,才能拿钱;相互僵持着)
package com.cwk.inner;
class Test implements Runnable
{
private int i ;
public Test(int i){
this.i = i;
}
public void run() {
if(i == 1){
while(true){
synchronized(DB.one){
System.out.println(Thread.currentThread().getName() + "-->one");
synchronized (DB.two) {
System.out.println(Thread.currentThread().getName() + "-->two");
}
}
}
}else{
while(true){
synchronized(DB.two){
System.out.println(Thread.currentThread().getName() + "-->two");
synchronized (DB.one) {
System.out.println(Thread.currentThread().getName() + "-->one");
}
}
}
}
}
}
class DB{
public static Object one = new Object();
public static Object two = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(1));
Thread t2 = new Thread(new Test(0));
t1.start();
t2.start();
}
}