操作系统是以进程为单位的,而进程是以线程为单位的,进程中必须有一个主线程main-----main是一个线程
进程:每个独立执行的程序称为进程
Java虚拟机允许应用并发的运行多个执行路劲(线程)
线程和进程的区别
每个进程独有地理的代码和数据空间(进程上下文--也就是进程所在容器),进程间的切换开销大
线程:同一进程内的线程共享和数据空间,线程切换到的开销小
多进程:在操作系统中能同时运行多个任务(程序)
多线程:在同一应用程序中多条执行路径同时执行
一般来说程序总是顺序执行的,但是这样很浪费时间,为此线程诞生了。线程是为了几个程序块能够同时运行(由于计算机分配时间的片段不同,每次刷新后,进程运行的顺序可能不同(个人观点)),而不要依次等待程序的顺序执行。
创建线程有两种方法:推荐使用第二种方法
一:创建一个类,让这个类继承Thread(线程),并复写Thread的run()方法,把需要让线程运行的代码全部放在run方法里面。
启动线程时,仅需要让这个类的实例去调用线程start方法就可以运行了。(不能直接调用run方法,因为只有调用start()方法启动,才是线程)
二:创建一个类,让它实现Runnable类,并实现它的run()方法。由于Runnable类就只有一个run方法,所以它没有办法通过自己去运行线程(因为它没有start方法),所以要运行它,就得用Thread的实例去包装它的实例,最后调用Thread的start方法启动。
线程方法:
getName() 获得当前线程的名称 Thread.currentThread().getName();
getId() 获得当前线程的id
sleep() 让当前线程休眠(参数为毫秒)静态方法
yield() 该方法为静态放方法,调用该方法后,只是让该线程暂停一下,不会阻塞该线程,而是让线程直接进入就绪状态。停了一下后,与该线程优先级相同或者高于他的就有可能获得执行机会。但是也有可能该线程暂停之后又立马获得运行机会(因为它也可以参与争夺cpu的时间段)。
join() 在当前线程中调用另一个线程的join()方法,则当前线程转为waiting状态,直到另一个线程运行结束,当前线程再由阻塞状态转为就绪状态。例如:在main方法中调用别的线程的join方法 Thread1.join();那么就得等着Thread1运行结束后,main才能再运行了。
线程调度
Thread:
join:线程的加入
sleep:线程的休眠
yield:线程的礼让
Object:必须要同步,必须在同步方法里或者同步代码块中
obj.wait() -->挂起当前线程,释放obj对象的锁
obj.notify() -->从被obj锁锁住的线程池里随机激活一个
obj.notifyAll()-->从被obj锁锁住的线程池里激活所有
Object类中的wait()等方法,导致当前的线程等待,知道其他线程调用此对象的notify()方法 或 notifyAll()唤醒方法
Object类中的wait()等方法,导致当前的线程等待,知道其他线程调用此对象的notify()方法 或 notifyAll()唤醒方法
线程唤醒
Object 类中notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程堵在此对象上等待,则会选择唤醒其中一个线程,选择是任意的。Object类中的notifyAll()方法,唤醒在此对象监视器上等待的所有线程。
wait() notify() notifyAll()这三个方法只能 在被同步化(synchronized)的方法或代码块中调用
线程的停止
如果线程的run()方法中执行的是一个重复执行的循环,可以提供一个标记类控制循环是否执行。
如果线程因为执行sleep() 或是 wait() 而进入阻塞状态,此时想要停止它,可能使用interrupt(),程序会抛出InterruptException异常(捕获该异常)
如果程序因为输入/输出的等待而阻塞,基本上必须等待输入/输出的动作完成才能离开阻塞状态。无法用interrupt()方法来使得线程离开run()方法,要想离开,只能通过引发一个异常。
线程具有五种状态:创建(New)、就绪(Runnable-也称为可运行状态)、运行(Running)、阻塞(Blocked)、终止(Dead)
1.新建状态(New)
新创建了一个线程对象。
2.就绪状态(Runnable)
线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运 行线程池中,变得可运行,等待获取CPU的使用权。
3.运行状态(Running)
执行run()。
4阻塞状态
(Blocked)
阻塞状态是线程因为某种原因放弃
CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
–
同步阻塞:,若该同步锁被别的线程占用,则
JVM
会把该线程放入锁池
中。运行的线程在获取对象的同步锁时
–
等待阻塞:运行的线程执行
wait()
方法,
JVM
会把该线程放入等待池中。
–
其他阻塞:运行的线程执行
sleep()
或
join()
方法,或者发出了
I/O
请求
时,
JVM
会把该线程置为阻塞状态。当
sleep()
状态超时、
join()
等待线
程终止或者超时、或者
I/O
处理完毕时,线程重新转入就绪状态。
5结束状态
(Dead)
线程执行完了或者因异常退出了
run()方法,该线程结束生命周期。
线程的优先级
Java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程。优先级较高的线程会获得更多的运行机会
Thread类的setPriority() 和 getPriority() 方法分别用来设置和获取线程的优先级
线程同步
编程技巧,在方法中尽量少操作成员变量,多使用局部变量
synchronized:同步锁--当方法或代码块只用该关键字定义后,仅有一个线程能够进入到该代码块或方法。这两种方法使用后的效果是一样的,但是推荐使用第二种(因为有可能一个方法中,并不是所有的代码都是需要同步的)
例如:public synchronized void fun(){}
例如:synchronized (this) {}
在使用同步代码块的时候,参数中写的是,需要锁着的内容。
线程同步
编程技巧,在方法中尽量少操作成员变量,多使用局部变量
synchronized:同步锁--当方法或代码块只用该关键字定义后,仅有一个线程能够进入到该代码块或方法。这两种方法使用后的效果是一样的,但是推荐使用第二种(因为有可能一个方法中,并不是所有的代码都是需要同步的)
例如:public synchronized void fun(){}
例如:synchronized (this) {}
在使用同步代码块的时候,参数中写的是,需要锁着的内容。
在使用多线程的时候,如果是继承Thread类,并且启动多个线程的时候,那么在synchronized()中的小括号中则不能直接写this,因为,继承Thread类创建的线程,是new Thread();的,所有每一个线程的对象都是不一样的,synchronized(this)只是锁着当前对象,所以达不到锁住所需内容的目的,因为每一个线程都是不同的对象。所以必须写具体的需锁住的内容,或者写当前类的类名点class,(如:MyRun.class)获取当前类的对象。实现Runnable接口的可以直接写。
死锁:
发生死锁的条件
线程手中占有资源,却都在等待着彼此手里的资源,这中无限制的等待造成程序一直运行无法结束-称之为死锁
线程的安全性:
一个类是线程不安全的---是指:当一个线程在运行此部分代码时,其他线程也能够进来运行该代码,造成重复运行等,解决办法是使用线程同步。
线程安全的是指---该类已实现过线程同步