java复习之多线程
进程:是一个独立的运行环境,可看作一个程序或者一个应用。
线程:是在进程中执行的一个任务,是进程的子集,每条线程并行执行不同的任务,是进程的最小执行单位。
要想知道什么是多线程,首先我们要知道进程和线程之间的联系:进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守候线程都结束运行后才能结束。
程序的每一个部分都称作一个线程,并且每个线程定义了一个独立的执行路径。
而多线程是多任务的一种特别的形式,是实现并发的一种有效手段,多线程指的是这个程序(一个进程)运行时产生布置一个线程。多线程就只有一个目的:更好的利用CPU的资源。
多线程创建和启动
Java提供两种方式实现多线程,一种是通过继承java.long.Thread类来实现,一种是通过实现Runnable接口实现
1.继承 Thread类创建
只需要重写该类的run方法。
参考:
public class ThreadTest1 extends Thread{
private int i=0;
public void run(){
for(i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==2){
Thread myThread1=new ThreadTest1();
Thread myThread2=new ThreadTest1();
myThread1.start();
myThread2.start();
}
}
}
}
输出:
main 0
main 1
main 2
Thread-0 0
Thread-1 0
Thread-1 1
Thread-1 2
Thread-0 1
Thread-0 2
由上述代码可以看到,通过currenTread()获得主线程的引用,通过重写run()方法定义了一个ThreadTest1 类继承至Tread类。在主线程中通过start()方法启动子线程和线程运行所需要的框架。
2. 实现Runnable接口创建线程
Java不支持多继承,如果用户的类已经继承了一个类,而又需要使用多线程,那使用继承Tread类就不现实了,所以在这里要用到一个新的方法,Runnable接口。
public class ThreadTest1 implements Runnable{
int i;
//定义run方法
public void run(){
//线程内容
while(true){
System.out.println("Runnable"+i++);
if(i==3){
break;
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//实例化一个线程
ThreadTest1 r=new ThreadTest1();
Thread t=new Thread(r);
//对run方法进行调用
t.start();
//主线程内容
for(int i=0;i<5;i++){
System.out.println("主线程");
}
}
}
运行结果:
主线程
主线程
主线程
主线程
主线程
Runnable0
Runnable1
Runnable2
通过Runnable接口来实现多线程,需要借助于Tread类,因为Runnable接口没有提供任何东西支持多线程。
多线程的状态
如图所示:
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程的优先级
每一个Java线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
public class ThreadTest {
public static void main(String[] args) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
ThreadTest1 hi=new ThreadTest1(Thread.NORM_PRIORITY+2);
ThreadTest1 lo=new ThreadTest1(Thread.NORM_PRIORITY-2);
hi.start();
lo.start();
try {
Thread.sleep(10000);//间隔10000毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("主线程异常");
}
lo.stop();
hi.stop();
try {
hi.t.join();
lo.t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("异常");
}
System.out.println("低优先级:"+lo.ThreadTest1);
System.out.println("高优先级:"+hi.ThreadTest1);
}
}
public class ThreadTest1 implements Runnable{
int ThreadTest1=0;
Thread t;
//应用关键字volatile
private volatile boolean running=true;
public ThreadTest1(int p){
t=new Thread(this);
t.setPriority(p);
}
public void run(){ //线程运行
while(running){
ThreadTest1++;
}
}
public void stop(){ //线程结束
running=false;
}
public void start(){//线程开始
t.start();
}
}
可以通过Thread类的setPriority()方法设置线程的优先级,该方法的参数为int型,其实Java提供了三个优先级别,他们都为Thread类的常量,从高到低依次为:
- Thread.MAX_PRIORITY
- Thread.NORM_PRIORITY
- Thread.MIN_PRIORITY
设置线程优先级不会造成死锁的发生
Java也允许在任何时候调用getPriority()方法获得线程的优先级。
线程的同步
同步方法
对共享资源进行访问的方法定义中加上synchronized关键字修饰,使得此方法称为同步方法。可以简单理解成对此方法进行了加锁,其锁对象为当前方法所在的对象自身。多线程环境下,当执行此方法时,首先都要获得此同步锁(且同时最多只有一个线程能够获得),只有当线程执行完此同步方法后,才会释放锁对象,其他的线程才有可能获取此同步锁,以此类推…
要创建同步方法,只需要简单地在方法声明前加上关键字synchronized即可,比如:
public synchronized void run() {
// ....
}
当方法被声明为同步后,可以保证在同一对象上对同步的方法的两次交错调用这种情况不可能发生。当一个线程执行对象上的同步方法时,所有其他调用同一个对象同步方法的线程会阻塞(挂起执行),直到第一个线程完成对该对象同步方法的调用。