Java学习心得之线程(一)

一、线程简介

       线程是一个程序里面不同的执行路径。

    每一个分支都叫做一个线程,main()叫做主分支,也叫主线程。

  进程只是一个静态的概念,机器上的一个.class文件,机器上的一个.exe文件,这个叫做一个进程。程序的执行过程都是这样的:首先把程序的代码放到内存的代码区里面,代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已经产生了,但还没有开始执行,这就是进程,所以进程其实是一个静态的概念,它本身就不能动。平常所说的进程的执行指的是进程里面主线程开始执行了,也就是main()方法开始执行了。进程是一个静态的概念,在我们机器里面实际上运行的都是线程。

  Windows操作系统是支持多线程的,它可以同时执行很多个线程,也支持多进程,因此Windows操作系统是支持多线程多进程的操作系统。LinuxUinux也是支持多线程和多进程的操作系统。DOS就不是支持多线程和多进程了,它只支持单进程,在同一个时间点只能有一个进程在执行,这就叫单线程

  CPU难道真的很神通广大,能够同时执行那么多程序吗?不是的,CPU的执行是这样的:CPU的速度很快,一秒钟可以算好几亿次,因此CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,再下一个时间片又执行其他人一会,虽然有几十个线程,但一样可以在很短的时间内把他们通通都执行一遍,但对我们人来说,CPU的执行速度太快了,因此看起来就像是在同时执行一样,但实际上在一个时间点上,CPU只有一个线程在运行。

学习线程首先要理清楚三个概念:

  1. 进程:进程是一个静态的概念
  2. 线程:一个进程里面有一个主线程叫main()方法,是一个程序里面的,一个进程里面不同的执行路径。
  3. 在同一个时间点上,一个CPU只能支持一个线程在执行。因为CPU运行的速度很快,因此我们看起来的感觉就像是多线程一样。

  什么才是真正的多线程?如果你的机器是双CPU,或者是双核,这确确实实是多线程。

    Java的线程是通过java.lang.Thread类来实现的,JVM启动时会有一个main方法所定义的主线程,可以通过创建Thread实例来创建线程,每个线程都是由某个特定Thread对象对应的run方法来操作完成的,通过调用Thread类的start方法来启动一个线程。


二、线程的创建和启动

Java中创建线程的方法有两种如下:

1.继承Thread类

public class ThreadTest {
	/*
	 * 通过继承Thread类来启动线程
	 */
	public static void main(String[] args) {
		MyThread m1 = new MyThread();
		MyThread m2 = new MyThread();
		m1.start();
		m2.start();
	}
}
/*
 * MyThread继承自Thread线程类,通过实例化MyThread对象,调用start()方法
 * 来启动新线程
 * @see java.lang.Thread#run()
 */
class MyThread extends Thread{
	/*
	 *重写run方法,运行新线程逻辑
	 * @see java.lang.Thread#run()
	 */
		@Override
	public void run() {
		String threadName = currentThread().getName();//获取当前线程名称
		for(int i=0;i<10000;i++){
			System.out.println(threadName+":"+i);
		}
	}
	
}

部分输出结果:

Thread-0:5987
Thread-0:5988
Thread-1:8733
Thread-0:5989
Thread-0:5990
Thread-0:5991
Thread-1:8734
Thread-0:5992
Thread-0:5993

我们看到,代码中出现线程0和线程1交替输出,说明两个线程同时在运行(如果效果不明显,可以加大循环次数)

2.实现Runnable接口

public class RunnableTest {
	public static void main(String[] args) {
		MyThread1 m1 = new MyThread1("线程0");
		MyThread1 m2 = new MyThread1("线程1");
		/*
		 * 要启动一个新的线程就必须new一个Thread对象出来,不能直接调用run()方法
		 */
		new Thread(m1).start();
		new Thread(m2).start();
	}
}
/*
 * 通过实现Runnable接口来创建新线程
 */
class MyThread1 implements Runnable{
	private String threadName;//线程名称
	public  MyThread1(String threadName) {
		this.threadName = threadName;
	}
	/*
	 * 重写Run方法
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		for(int i=0;i<10000;i++){
			System.out.println(threadName+":"+i);
		}
	}
	
}

输出结果:
线程0:5742
线程0:5743
线程0:5744
线程1:5043
线程1:5044
线程1:5045
线程1:5046
线程1:5047
线程1:5048
线程1:5049
线程1:5050
线程0:5745
线程0:5746
线程0:5747

       使用实现Runnable接口和继承Thread类这两种开辟新线程的方法的选择应该优先选择实现Runnable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程。


三、线程状态转换



1.线程控制的基本方法


2.sleep/join/yield方法简介

sleep方法

public class SleepTest {
	public static void main(String[] args) {
		MyThread2 m1 = new MyThread2();
		m1.start();
		try {
			/*
			 * Thread.sleep(10000);
			 * sleep()方法是在Thread类里面声明的一个静态方法,因此可以使用Thread.sleep()的格式进行调用,
			 * 在哪个线程里面调用了sleep()方法就让哪个线程睡眠, 所以这里是让主线程睡眠10秒种.
			 */
			Thread.sleep(10000);
			 System.out.println("main线程睡眠了10秒种后再次启动了");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
			m1.flag = false;//改变循环条件,结束死循环
		}
	}
}

class MyThread2 extends Thread{
	public boolean flag=true;//控制循环条件
	@Override
	public void run() {
		//死循环,每秒打印系统时间
		while(flag){
			System.out.println(new Date().toString());
			try {
				/*
				 * 静态方法的调用格式一般为“类名.方法名”的格式去调用 在本类中声明的静态方法时调用时直接写静态方法名即可。 当然使用“类名.方法名”的格式去调用也是没有错的,
				 * 使用“类名.方法名”的格式去调用属于本类的静态方法
				 */
				sleep(1000);
				/*
				 * 睡眠的时如果被打断就会抛出InterruptedException异常
				 */
			} catch (InterruptedException e) {
				/*
				 *  // 线程被中断后就返回,相当于是结束线程
				 */
				return;
			}
		}
	}
	
}
输出结果:

Tue Oct 24 11:57:49 CST 2017
Tue Oct 24 11:57:50 CST 2017
Tue Oct 24 11:57:51 CST 2017
Tue Oct 24 11:57:52 CST 2017
Tue Oct 24 11:57:53 CST 2017
Tue Oct 24 11:57:54 CST 2017
Tue Oct 24 11:57:55 CST 2017
Tue Oct 24 11:57:56 CST 2017
Tue Oct 24 11:57:57 CST 2017
Tue Oct 24 11:57:58 CST 2017
main线程睡眠了10秒种后再次启动了


join方法
public class TestThread4 {
    public static void main(String args[]) {
        MyThread2 thread2 = new MyThread2("mythread");
        // 在创建一个新的线程对象的同时给这个线程对象命名为mythread
        thread2.start();// 启动线程
        try {
            thread2.join();// 调用join()方法合并线程,将子线程mythread合并到主线程里面
            // 合并线程后,程序的执行的过程就相当于是方法的调用的执行过程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i <= 5; i++) {
            System.out.println("I am main Thread");
        }
    }
}

class MyThread2 extends Thread {
    MyThread2(String s) {
        super(s);
        /*
         * 使用super关键字调用父类的构造方法 
         * 父类Thread的其中一个构造方法:“public Thread(String name)” 
         * 通过这样的构造方法可以给新开辟的线程命名,便于管理线程
         */
    }

    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("I am a\t" + getName());
            // 使用父类Thread里面定义的
            //public final String getName(),Returns this thread's name.
            try {
                sleep(1000);// 让子线程每执行一次就睡眠1秒钟
            } catch (InterruptedException e) {
                return;
            }
        }
    }
}


yield方法

public class TestThread5 {
    public static void main(String args[]) {
        MyThread3 t1 = new MyThread3("t1");
        /* 同时开辟了两条子线程t1和t2,t1和t2执行的都是run()方法 */
        /* 这个程序的执行过程中总共有3个线程在并行执行,分别为子线程t1和t2以及主线程 */
        MyThread3 t2 = new MyThread3("t2");
        t1.start();// 启动子线程t1
        t2.start();// 启动子线程t2
        for (int i = 0; i <= 5; i++) {
            System.out.println("I am main Thread");
        }
    }
}

class MyThread3 extends Thread {
    MyThread3(String s) {
        super(s);
    }

    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(getName() + ":" + i);
            if (i % 2 == 0) {
                yield();// 当执行到i能被2整除时当前执行的线程就让出来让另一个在执行run()方法的线程来优先执行
                /*
                 * 在程序的运行的过程中可以看到,
                 * 线程t1执行到(i%2==0)次时就会让出线程让t2线程来优先执行 
                 * 而线程t2执行到(i%2==0)次时也会让出线程给t1线程优先执行
                 */
            }
        }
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值