多线程的基本实现
进程指运行中的程序,每个进程都会分配一个内存空间,一个进程中存在多个线程,启动一个JAVA虚拟机,就是打开个一个进程,一个进程有多个线程,当多个线程同时进行,就叫并发。
J ava创建线程的两种方式为: 继承Thread类 和实现Runnable接口
Thread类
1、通过覆盖run方法实现线程要执行的程序代码
2、Start()开始执行多线程
package com.bin.duoxiancheng;
public class d1 extends Thread{
public void run(){
for(int i=0 ; i<50; i++){
System.out.println(i);
System.out.println(currentThread().getName());
try {
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
public static void main(String[] args){
new d1().start();
new d1().start();
}
}
多个线程共享一个实例的时候,代码代码如下:
package com.bin.duoxiancheng;
public class d1 extends Thread{
int i=0;
public void run(){
for(i=0 ; i<50; i++){
System.out.println(i);
System.out.println(currentThread().getName());
try {
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
public static void main(String[] args){
new d1().start();
new d1().start();
}
}
结果如下所示:
0
Thread-1
0
Thread-0
1
Thread-1
1
实际2个线程在操纵不同的变量a,在执行run方法时候,线程把a都当做自己的变量在执行。
Runnable接口实现多线程
当一个继承自Thread时,就不能再继承其他类,使用Runnable接口解决了此问题,在新建一个Thread类中,在构造方法中初始化
| Thread(Runnable target) |
| Thread(Runnable target,String name) |
package com.bin.duoxiancheng;
public class D2 implements Runnable{
int i=0;
public void run(){
for(i=0 ; i<50; i++){
System.out.println(i);
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
public static void main(String[] args){
D2 d=new D2();
Thread t=new Thread(d);
t.start();
}
}
线程中状态之间的转变
New状态,使用new创建的线程对象处于新建状态,仅仅在堆栈中分配了内存。
Runnable就绪状态。当线程调用start方法后,线程进入就绪状态。虚拟机会为他创建方法调用栈和程序计数器,处于这个状态的线程位于可运行池中,等待获得CPU使用权。
Running运行状态
这个状态的线程正在占用CPU,如果一个计算机只有一个CPU,那么这个时刻会有一个线程处于这个状态,如多个CPU,可有多个线程占用不同的CPU,只有处于就绪状态的线程才有机会变为运行状态。
阻塞状态(Blocked)
由于某些原因放弃CPU暂时停止运行,此时虚拟机不会给线程分配CPU,直到重新进入就绪状态,
阻塞状态可分为以下3种。
1、 当处于运行状态中的某个对象执行了wait()方法,虚拟机就把这个线程放到这个对象等待池中。
2、 当线程处于运行状态,试图获得某个对象的同步锁,若同步锁被其他线程占用,虚拟机会把这个线程放到这个对象的锁池中。
3、 当线程执行了sleep方法,或者调用join方法,或者发出了I/O请求时候,就会进入这个状态.
当线程执行System.out.println或者System.in.read()方法时,就会发出I/O请求,线程放弃cpu进入阻塞状态,直到I/O处理完成,
死亡状态(dead)
当线程退出run()方法时,就进入死亡状态,有可能正常 执行完run方法退出,也有可能异常退出,
Thread类中的isAlive()方法判断一个线程是否活着,当处于死亡状态或者新建状态时,该方法返回false,否则返回TRUE。
线程的调度
虚拟机按特定的机制为CPU分配使用权,主要有两种调度模式,分时调度模型和抢占式调度模型。分时调度模型就是线程轮流让CPU获得使用权,平均分配线程。抢占式调度为让线程中优先度高的占用CPU,若优先级相同,那么就随机选择一个。处于运行状态的线程会由以下原因放弃CPU
1、虚拟机转到就绪状态,其他线程获得机会。
2、线程进入阻塞状态;
3、线程运行结束
看以下程序:
package com.bin.duoxiancheng;
public class D3 extends Thread {
public static int count=0;
public static StringBuffer log=new StringBuffer();
public void run(){
for(int a=0;a<20;a++){
log.append(currentThread().getName()+":"+a+" ");
if(++count %10 ==0)log.append("\n");
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generatedmethod stub
D3 d1= new D3();
D3 d2 =new D3();
d1.setName("m1");
d2.setName("d2");
d1.start();
d2.start();
while(d1.isAlive()||d2.isAlive())
Thread.sleep(500);
System.out.print(log);
}
}
结果为:
m1:0 m1:1 m1:2 m1:3 m1:4 m1:5m1:6 m1:7 m1:8 m1:9
m1:10 m1:11 m1:12 m1:13 m1:14m1:15 m1:16 m1:17 m1:18 m1:19
m2:0 m2:1 m2:2 m2:3 m2:4 m2:5m2:6 m2:7 m2:8 m2:9
m2:10 m2:11 m2:12 m2:13 m2:14 m2:15 m2:16m2:17 m2:18 m2:19
程序中m1先获得主动权,直到结束才交予m2,如果想让一个线程交给另外一个线程运行可以采取以下方法:
1、 调整优先级;调用Thread.setPriority(Thread.MAX_PRIORITY),参数取值范围为1-10,值越高优先度越高。
2、 调用Thread.sleep()方法;参数以毫秒为单位,当睡眠结束时候就会转为就绪状态,当另一个线程处于运行状态中,此线程在运行池 中处于等待状态
3、 调用Thread.yield()方法,如有有相同优先级别的线程处于就绪状态,那么yield把当前运行的线程放到可运行池中并使另一个线程运行;若没有相同级别的,则不运行,执行yield后,当前线程进入就绪状态,跟SLEEP不同,sleep为进入阻塞状态。
4、 调用JOIN方法;使用此方法后,当前程序进入阻塞状态。直至另一个程序结束才会恢复 运行。
package com.bin.duoxiancheng;
public class D5 extends Thread{
public void run(){
for(int a=0;a<50;a++){
System.out.println(getName()+":"+a);
}
}
public static void main(String[] args) throws InterruptedException {
D5 d=new D5();
d.setName("m1");
d.start();
System.out.println("main: joinmachine");
d.join();
System.out.println("main:end");
}
}
System.out.println("main:end");这段程序要等到d这个线程结束后方执行。
也可以写作d.join(10)超过10毫秒恢复运行。
Timer类定时器的使用
参数使用如下:
(1)Timer.schedule(TimerTask task,Datetime)安排在制定的时间执行指定的任务。
(2)Timer.schedule(TimerTask task,Date firstTime ,long period)安排指定的任务在指定的时间开始进行重复的固定延迟执行.
(3)Timer.schedule(TimerTask task,long delay)安排在指定延迟后执行指定的任务.
(4)Timer.schedule(TimerTask task,long delay,long period)安排指定的任务从指定的延迟后开始进行重复的固定延迟执行. 第一个参数是要操作的方法,第二个参数是要设定延迟的时间,第三个参数是周期的设定,每隔多长时间执行该操作。
(5)Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period)安排指定的任务在指定的时间开始进行重复的固定
速率执行.
(6)Timer.scheduleAtFixedRate(TimerTask task,l