以前学习基础的时候学习了一段时间的多线程,那时候感觉学的似懂非懂。因为到现在很长一段时间没有用到多线程的知识,所以现在基本上忘了差不多了。但现在已经准备年后换工作了,所以现在要特意又研究了一下多线程,被问到就尴尬了了,哈哈
ps:刚接触的时候觉得超级难,现在好多了,嘿嘿
1. 进程和线程的区别
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程是操作系统调度的最小单元,同时线程又是一种受限制的系统资源,即线程不能无限制产生。当系统中存在大量线程时,系统会通过时间片轮转的方式调度每个线程,因此线程不能做到绝对的并行,除非线程数量小于CPU的核心数,一般来说这是不可能的。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
2 线程的创建于启动
2.1 方法一
①实现java.lang.Runnable接口,重写run()方法,启动:new Thread(this).start()
public class ThreadTest1
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
//r.run();并不是线程开启,而是简单的方法调用
Thread t = new Thread(r);//创建线程
//t.run(); //如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
t.start(); //线程开启
for (int i = 0; i < 100; i++) {
System.out.println("main:"+i);
}
}
class MyRunnable implements Runnable{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread-----:"+i);
}
}
}
运行结果:
要注意的是:
1. r.run()并不是启动线程,而是简单的方法调用。
2. Thread也有run()方法,如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3. 并不是一启动线程(调用start()方法)就执行这个线程,而是进入就绪状态,什么时候运行要看CUP。
2.2 方法二
②继承java.lang.Thread类,重写run()方法。
public class TestThread2
public static void main(String[] args) {
Thread1 t = new Thread1();
//t.run(); //这里也不能直接调用方法
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("main:"+i);
}
}
}
//尽量使用实现Runnnable接口,因为接口比较灵活
class Thread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread-----:"+i);
}
}
}
虽然两种方法都可行,但是最好 还是用第一种方法,因为使用接口灵活性好,java中是单继承、多实现。
3. 线程状态转换
4. 线程控制的基本方法
4.1 sleep(long millis)
public class Test
public static void main(String[] args) {
Thread2 t = new Thread2();
t.start();
try {
Thread.sleep(10000); //主线程睡眠10秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程睡眠10秒钟后结束t线程
//t.interrupt(); //这种结束方式比较粗暴,如果t线程打开了某个资源还没来得及关闭也就是run方法还没有执行完就强制结束线程,会导致资源无法关闭
//t.stop();也是结束某个线程,这种方式比interrupt()更粗暴
t.flag = false;
}
}
class Thread2 extends Thread{
boolean flag = true;
//用这种方式结束线程很不错,用一个变量控制run方法什么时候不再执行,不会出现run方法没有执行完毕就结束
@Override
public void run() { //run方法一结束,整个线程就终止了
while(flag){
System.out.println("---"+new Date()+"---");
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
4.2 join()
指等待t线程终止。也可以理解为将t线程合并到当前线程来,等待t线程结束后再往下执行。相当于方法调用
package com.thread;
import java.util.Date;
/*
* t.join()方法指等待t线程终止。也可以理解为将t线程合并到当前线程来,等待t线程结束后再往下执行。相当于方法调用
*/
public class TestJoin
public static void main(String[] args) {
Thread t = new Thread3("abc");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("我是main线程");
if(i==10){
try {
t.join();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Thread3 extends Thread{
public Thread3(String s) { //给该线程取一个名字,用getName()方法可以去到该名字
super(s);
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是"+getName()+"线程");
try {
sleep(1000);
} catch
运行结果为:
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是main线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是abc线程
我是main线程
我是main线程
我是main线程
我是main线程
我是main线程
我是main线程
我是main线程
我是main线程
我是main线程
可以看到从第22行起就开始顺序执行了,因为i=10的时候就将该形成合并了。
4.3 yield() - - 线程礼让,让出CPU,进入就绪状态
4.4 interrupt() - - 中断某个线程
这种结束方式比较粗暴,如果t线程打开了某个资源还没来得及关闭也就是run方法还没有执行完就强制结束线程,会导致资源无法关闭
要想结束进程最好的办法就是用sleep()函数的例子程序里那样,在线程类里面用以个boolean型变量来控制run()方法什么时候结束,run()方法一结束,该线程也就结束了