线程概述
线程是程序运行的基本执行单位,当操作系统执行一个程序时,会创建一个进程,而这个进程至少创建一个线程(主线程)作为这个程序的入口点。所以,在操作系统中运行的程序都至少有一个主线程。
进程和线程是现代操作系统必不可少的运行模型,在操作系统中可以有多个进程,这些进程包括系统进程(系统内部创建的进程)和用户进程(用户程序创建的进程),一个进程可以有多个线程。进程之间不存在内存共享,就是说,系统中的进程都是在各自的内存空间中运行的。但是一个进程中的线程可以共享系统分配给这个进程的内存空间。
线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,叫线程栈。是在建立线程时系统分配的,主要是保护线程内部所使用的数据。
创建线程的方法
在Java中创建线程的方法有两种:使用 Thread类和使用Runnable接口。
-
使用thread类创建
如下所示:
NewThth=new NewTh();
NewTh th1=new NewTh();
Thread td=new Thread(th);
Threadtd1=new Thread(th1);
td.start();
td1.start();
这样就启动了线程。
一个普通的Java类只要继承了thread类,就可以成为一个线程类,如下面的代码:
packagecom.cn.test;
//线程就是为了更好地利用CPU,提高程序运行速率的!
publicclass MyThread extends Thread{
private int i ;
public void run(){
for (int i = 0;i<100;i++){
System.out.println(Thread.currentThread().getName()+""+i);
}
}
public static void main(String[] args) {
new MyThread().run();
//run()方法当作普通方法的方式调用,程序还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。
new MyThread().start();
//start()方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。
new MyThread().start();
}
}
-
使用Runnable接口
使用接口Runnable创建一个线程时,需要调用该对象的run方法。
如代码:
packagecom.cn.test;
//线程就是为了更好地利用CPU,提高程序运行速率的!
publicclass MyThread1 {
public static void main(String[] args) {
NewTh th=new NewTh();
NewTh th1=new NewTh();
Thread td=new Thread(th);
Thread td1=new Thread(th1);
th.run();
th1.run();
//th.start(); th对象没有start()方法,在这里run()方法相当于方法的调用,
td.start();
td1.start();
td.run();
td1.run();
}
}
classNewTh implements Runnable{
int i=0;
@Override
public void run() {
// TODO Auto-generated method stub
for(int i =0 ;i<20;i++){
System.out.println(Thread.currentThread().getName()+"" +i);
}
System.out.println("金丝燕,未来无极限");
}
}
-
线程的启动
通过以上的例子,可以发现继承了Thread类的线程启动执行start()方法和run()方法,而实现Runnable 接口的只能执行run()方法,而这两个有什么区别那?
Start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体执行完毕而直接继续执行下面的代码。通过Thread类的start()方法来启动一个线程,这时线程就会处于可运行状态,但并没有运行,一旦得到CPU时间片段就开始执行run方法,这时run()称为线程体,它包含了这个线程要执行的内容,Run方法执行结束,此线程终止。
Run:
run()方法只是类的一个普通方法,如果调用run方法,那么只用等run方法执行完毕才能执行下面的代码。
线程的状态
线程中除了有start和run方法来操作线程的状态,还有一些其他的方法来控制。
-
睡眠线程
sleep(long millis)方法
让线程停止一段时间,在sleep时间间隔期满后,线程不一定立即恢复执行,因为在这一时刻,其他线程正在运行而没有被调度为放弃执行,除非恢复过来的线程具有更高的优先级,或者正在运行的线程因为其他原因而被阻塞。sleep(long millis,int nanos)方法,
millis
- 以毫秒为单位的休眠时间,nanos- 要休眠的另外 0-999999 纳秒。如代码:public classMyThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
if ((i) % 10 == 0) {
System.out.println("-------" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.println(" 线程睡眠1秒!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new MyThread().start();
}
}
wait()方法
让线程等待一段时间,或者是在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前等待
等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。
notify()方法
唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
调用任意对象的notify()方法则导致因调用该对象的wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。
notifyAll()
唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。
resume()方法和suspend()
suspend()方法使得线程进入阻塞状态,并且不会自动恢复,必须调用其对应的resume()方法才能使得线程重新进入可执行状态。
resume方法和suspend方法在一起使用,具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源,如果重新开始目标线程的线程想在调用resume之前锁定该监视器,则会发生死锁,这类死锁通常会证明自己是“冻结”的进程。
stop()
stop()方法是不安全的,该方法已经过时,在调用Thread.stop方法的时候,会即刻抛出ThreadDeath异常,同时会释放该线程的所有的锁。调用stop方法会产生不完整的残废数据,而多线程编程最最基础的就是保证数据的完整性,所以不建议使用stop方法。
interrupt()
请移步http://blog.csdn.net/cxy782255346/article/details/38472851
isAlive()
用于测试线程是否处于活动状态,如果线程已经启动且尚未终止,则为活动状态。如果该线程处于活动状态则返回TRUE,否则返回FALSE。
join()
join()用于停止当前线程而运行别的线程。换句话说就是,join()方法可以把两个交替执行的线程合并为顺序执行的线程,比如在线程B中调用了线程A中的join()方法,那么就会等到线程A执行完之后,B线程才会继续执行。