一、线程基本概念
1、 进程 Processor
一块包含了某些资源的独立内存空间,用于存放资源。
2、 线程 Thread
线程是在进程内部同时做的事情,是CPU能够调度的最小单位,一个进程包含至少一个线程,线程与线程是独立的,共享进程里面的资源。
3、单核CPU:同一时间只能干一件事情。
4、CPU调度算法
① 分时调度:比如每个运行50ns;
② 抢占式调度:谁抢到CPU的资源就执行谁,调度算法。JVM虚拟机里面的线程采用抢占式调度。
二、Java中创建线程方式
1、继承Thread类,重写run( )方法
(1)步骤:
① 创建线程对象
② 开启线程,调用 start( ) 方法
(2)Thread创建线程Demo
① 创建一个MyThread1类继承Thread类,
public class MyThread1 extends Thread {
//创建一个run( )方法
public void run ( ) {
for( int i = 0 ; i < 1000 ; i++ ) {
System.out.println( "这是继承实现的线程" );
}
}
}
② 在主方法中创建线程对象并调用方法。
public class Demo1{
public static void main(String[] args) {
//创建线程对象
MyThread1 mt = new MyThread1( );
//开启线程,调用start( ) 方法,start( ) 会自动调用MyThread类中的run( ) 方法。
mt.start( );
//谁抢到CPU资源就运行谁
for( int j = 0 ; j < 1000 ; j++) {
System.out.println( "这是main( )方法中的线程" );
}
}
}
JVM虚拟机里面的线程采用抢占式调度,所以线程的执行是随机的,哪条线程抢到CPU的执行权,就执行哪条线程,所以上面程序的结果如下(只截取部分):
...
这是第二个线程2
这是第二个线程2
这是继承实现的线程
这是继承实现的线程
这是继承实现的线程
这是继承实现的线程
这是第二个线程2
这是第二个线程2
这是第二个线程2
...
2、实现Runable接口,重run( )方法
(1)Runable创建线程Demo1
① 创建一个类MyThread实现Runnable接口,
public class MyThread2 implements Runnable{
public void run ( ) {
for( int i = 0 ; i< 500 ; i++ ) {
System.out.println( "实现Runable接口创建线程");
}
}
}
② 在主方法中创建线程对象并调用方法,运行结果类似Demo1。
public class Demo2 {
public static void main(String[] args) {
MyThread2 mt2 = new MyThread2( );
Thread t2 = new Thread( mt2 );
t2.start( );
//谁抢到CPU资源就运行谁
for( int j = 0 ; j < 1000 ; j++) {
System.out.println( "这是main( )方法中的线程" );
}
}
}
(2)示例2
创建一个类继承Thread类
public class MyThread1 extends Thread {
//创建一个run( )方法
public void run ( ) {
for( int i = 0 ; i < 1000 ; i++ ) {
System.out.println( "这是继承实现的线程" );
}
}
}
创建一个类实现Runnable接口
public class MyThread2 implements Runnable{
public void run ( ) {
for( int i = 0 ; i< 500 ; i++ ) {
System.out.println( "实现Runable接口创建线程");
}
}
}
主函数类
public class Demo3 {
public static void main(String[] args) {
//线程1
MyThread1 mt = new MyThread1( );
//开启线程,调用start( ) 方法,start( ) 会自动调用MyThread类中的run( ) 方法。
mt.start( );
//主线程
for( int j = 0 ; j < 1000 ; j++) {
System.out.println( "这是main( )方法中的线程" );
}
//线程2
MyThread2 mt2 = new MyThread2( );
Thread t2 = new Thread( mt2 );
t2.start( );
}
}
运行结果(部分):
...
这是main( )方法中的线程
这是main( )方法中的线程
这是main( )方法中的线程
这是main( )方法中的线程
这是继承实现的线程
这是继承实现的线程
实现Runable接口创建线程
实现Runable接口创建线程
实现Runable接口创建线程
实现Runable接口创建线程
...
3、匿名内部类方式创建线程
(1)匿名内部类方式创建线程Demo
public class Demo {
//匿名内部类的方式创建线程
public static void main(String[] args) {
//第一种
new Thread() {
public void run() {
System.out.println( "线程执行的内容");
}
}.start();
//第二种
new Thread( new Runnable() {
public void run() {
System.out.println( "匿名内部类创建线程");
}
}).start();
}
}
三、线程的生命周期
1、相关概念
(1)新建状态:创建线程对象 new Thread( );
(2)就绪状态:调用start( )后进入就绪状态,就绪状态是进入运行状态的唯一入口(要想进入运行状态必须先进入就绪状态)
(3)运行状态:执行run( )方法里面的内容,获得CPU的使用权;
(4)阻塞状态:处于运行状态的线程,由于某种原因暂时放弃的对CPU的使用权,停止执行,如果想继续运行,必须进入就绪状态,重写争夺CPU资源;
- 等待阻塞:wait( );
- 同步阻塞:在等待获取锁资源的时候;
- 其他阻塞。
(5)死亡状态:线程执行完run( )方法里面的内容或者因异常退出,该线程生命周期结束。
四、Thread类
1、currentThread( )方法、sleep( )方法、setName( )方法
(1)currentThread( )方法
是返回对当前正在执行的线程对象的引用。
(2)sleep( )方法
① 是在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),sleep()是静态方法,只能控制当前正在运行的线程。
② 线程睡眠是帮助所有线程获得运行机会的最好方法。线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。
③ sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
MyThread类
public class MyThread4 extends Thread {
//创建一个run( )方法
public void run ( ) {
Thread td = Thread.currentThread( );
System.out.println( td); //得到Thread[改名后名字,5,main]
//线程执行的内容,输出100次"这是继承实现的线程"
try {
for( int i = 0 ; i < 100 ; i++ ) {
Thread.sleep(1000);
System.out.println( "这是继承实现的线程" );
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//构造函数
public MyThread4( ) { }
public MyThread4( String str ) {
super( str );
}
}
主函数类
public class Demo4 {
public static void main(String[] args) {
Thread ct = Thread.currentThread( );
System.out.println( ct ); //得到 Thread[main,5,main]
MyThread4 mt = new MyThread4( );
//修改名字
mt.setName( "改名后名字" );
//开启线程
mt.start( );
}
}
运行结果(部分)为:
...
Thread[main,5,main]
Thread[改名后名字,5,main]
这是继承实现的线程
这是继承实现的线程
这是继承实现的线程
...
这里,Thread[main,5,main]是主函数中Thread ct = Thread.currentThread( ); System.out.println( ct ); 语句得到的结果,Thread[改名后名字,5,main] 是主函数调用MyThread类中方法,其方法中的Thread td = Thread.currentThread( ); System.out.println( td); 语句执行得到的结果。
如果不改名字,初始结果应该是 Thread[Thread-0,5,main],其中:
Thread表示类,Thread-0表示线程名,5表示优先级(1-10,默认为5,1最低,10最高),优先级表示获取到CPU资源的概率
(3)setName( )方法是改变线程名称,使之与参数name相同
(4)yield()方法
- 暂停当前正在执行的线程对象,并执行其他线程。即抢到CPU执行器,但是放弃执行权,降低其概率。
- yield( )应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。
(5)jion()方法
join()是非静态方法,让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作,即让交替运行的线程按照顺序执行。
示例
public class Demo5 {
// join( )方法,让交替运行的线程按照顺序执行
public static void main(String[] args) {
//下载到100% 下载成功
Thread t1 = new Thread( ) {
public void run( ) {
for( int i = 0 ; i < 100 ; i ++ ) {
try {
Thread.sleep(100);
System.out.println( "下载完成" + i + "%" );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
Thread t2 = new Thread( ) {
public void run( ) {
try {
for( int j = 0 ; j < 10 ; j++) {
Thread.sleep(1000);
System.out.println( "我要下载了" );
}
t1.join( );
System.out.println( "下载完成" );
} catch (InterruptedException e) {
e.printStackTrace( );
}
};
};
t1.start( );
t2.start( );
}
}
五、守护线程
(1)在后台提供一种通用服务的线程,当所有通用线程结束后,该线程自动死亡。
(2)设置为守护线程的方法:setDaemon( boolean on ),将该线程标记为守护线程或用户线程。
(3)示例
public class Demo6 {
//守护线程示例
public static void main(String[] args) {
//通用线程
Thread t1 = new Thread( "德玛" ) {
public void run( ) {
for( int i = 0 ; i < 10 ; i++ ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace( );
}
String name = Thread.currentThread( ).getName( );
System.out.println( name + "说:我是德玛西亚" );
}
System.out.println( "我现在不是了" );
};
};
//守护线程
Thread t2 = new Thread( "EZ" ) {
public void run( ) {
while( true ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace( );
}
String name = Thread.currentThread( ).getName( );
System.out.println( name + "说:我是EZ" );
}
};
};
t2.setDaemon( true ); //设置为守护线程
t1.start( );
t2.start( );
}
}
注意:
- 设置守护线程必须在start( )方法前设置;
- 在守护线程内创建的新线程也是守护线程;
- 守护线程不会去访问固有资源或数据库等,因为该线程随时可能停止;
- 守护线程会一直概率性执行,直到其他通用线程执行完毕后停止。