线程不拥有系统资源,线程拥有自己的堆栈、计数器等,但是它是与进程的其他线程共享的资源,非自己拥有。
线程是独立运行的,并不知道是否还有其他线程存在,但可以编程通过共享方式进行通信。其调度方式为优先级+抢占。
由于进程不需要分配独立的进程空间,其效率更高。
进程的应用实例:
1、word的输入和输入时检查
2、浏览器同时下载多个图片
3、变下载边播放、展示
进程的状态:
newBorn:被创建但是未开始执行,可通过start方法启动,产生这个进程需要的系统资源。通过调用run方法进入Runnable状态
Runnable状态:线程处于就绪状态,在队列中根据优先级和先到先得等待处理器资源
Running状态:正在运行中,在run方法执行完成后退出运行状态。除非进程运行中主动放弃或者被更高优先级进程抢占,否则会一直运行下去。
Blocked状态:阻塞,进程处于这个状态无法进入就绪队列,需要被其他时间唤醒。—小心 可能进入死锁
Dead状态:执行完毕或者被强行中断。死了就是死了,不能通过start方法再运行。stop方法课强行停止,但是已经不被推荐使用。
进程从running退出的条件和方法
1、主动释放或者被更高优先级抢占
2、sleep方法和yield方法,yield只给相同优先级的执行机会。注:sleep结束后进入的是runnable状态 不是running
3、wait等待需要的变量的方法
4、输入输出发生线程阻塞
5、suspend方法 已经不再推荐使用
/**
* 使用继承使用线程的方式
* 1、写一个类继承Thread类,然后必须重写run方法
* 2、主线程中新建对象,然后run方法即可
*/
class CreatThread extends Thread { // Thread 属于lang包 无需单独引入
// 必须写run方法覆盖父类
public void run() {
System.out.println();
System.out.println("子线程开始:" + this);
System.out.println(this.getName()); //getName方法获取线程名字
for (int i = 0; i < 4; i++) {
System.out.println(this + "." + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
CreatThread thread1 = new CreatThread();
CreatThread thread2 = new CreatThread();
thread1.start();
thread2.start();
/**
* 子线程开始:Thread[Thread-1,5,main]
* 子线程开始:Thread[Thread-0,5,main]
* Thread-0
* Thread[Thread-0,5,main].0
* Thread-1
* Thread[Thread-0,5,main].1
* Thread[Thread-1,5,main].0
* Thread[Thread-0,5,main].2
* Thread[Thread-0,5,main].3
* Thread[Thread-1,5,main].1
* Thread[Thread-1,5,main].2
* Thread[Thread-1,5,main].3
*/
}
}
注:Thread.currentThread是静态方法,可返回当前正在执行的进程(可现实main进程)
getPriority和setPriority方法可获取和设置优先级
由于stop方法不再被推荐使用,那么如何实现进程的停止呢?
//只用一次时候 每次都要想类名很烦 所以需要匿名类
class X extends Thread{
private boolean running=false;
public void run(){
while(running){
do something
}
}
public void start(){
running=true;
super.start;
}
public void halt(){
running=false;
}
由于1、继承方式创建线程时,多个线程直接无法共享实例变量! 2、java只能单继承 而有些类如Frame或者Applet 必须被拓展
所以需要runnable接口创建进程
Runnable接口实现线程
public class ThreadTestByImplements implements Runnable{
/**
* implements实现进程的方法
* 1、继承Runnable接口
* 2、必须重写run方法
* 3、在构造器或者方法中,通过Thread name = new Thread(this)变为线程,然后name.start()的方式自动调用run方法
* 注,new Thread(this,"名字")可为线程指定名字
* 4、由于最好设置停止机制,所以写代码的时候可以使用下面这样的模板。
*/
private Thread threadName=null;
public void start(){
if(threadName==null){
threadName=new Thread(this);
threadName.start();
}
}
public void stop(){
threadName=null;
}
public run(){
//可通过currentThread获取进程名 然后判断名字相等
//do 具体逻辑
}
public static void main(String[] args) {
ThreadTestByImplements a = new ThreadTestByImplements();
a.start();
a.run();
a.stop();
}
}
实例:没秒输出一次时间,由于这个例子不能阻塞其他运行 因此最好使用进程来写
import java.util.Date;
public class ThreadTestByImplements implements Runnable{
private Thread clocker = null;
//构造方法中开启线程
public ThreadTestByImplements(){
clocker = new Thread(this);
clocker.start(); //需要手动写start方法 是和继承的不同之处
}
//必须run方法,具体逻辑写这里
public void run(){
while(true){
Date now = new Date();
System.out.println(now);
try{
clocker.sleep(1000);//也可直接Thread.sleep 这个是让当前运行的进程睡眠 实际就是clocker
}catch(InterruptedException e){
}
}
}
public static void main(String[] args) {
ThreadTestByImplements aBC = new ThreadTestByImplements();
}
}
Callable方法创建进程 书写的不好 暂不看
Daemon Thread ----后台进程、守护进程
JVM垃圾回收就是典型的后台线程
特征:如果所有的前台进程都死亡,后台进程也会死亡
通过setDaemon(true)方法指定为后台进程(必须再start方法之前调用)
线程安全—线程同步
问题引入:比如当同时两个人对一个账户进行存、取操作,当线程刚好在读取到余额准备扣钱的时候,或者判断余额是否足够的时候,进程切换了。那么会导致错误的结果
方法1
为此 使用 synchronized对线程加锁
使用方法1:方法申明为synchronized
使用方法2:synchronized(要锁定的参数变量){ 代码块}
管程(互斥锁)、Lock(同步锁)
线程通信
1、当使用Syn时候wait方法 等待其他线程notify()或者notifyAll() 然后才能继续
2、当使用Lock时,不能使用上面的方法 要用Condition控制
3、java5引入的阻塞队列
线程组
使用Tread的构造器实现 此处不赘述
线程池
系统启动一个新线程的成本是比较高的,因为它设计和操作系统的交互,当需要很多生存期很短暂的线程的时候,这种情况可以使用线程池,先预先创新大量空线程等待使用。