一、操作系统中线程和进程的概念
现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
“同时”执行是人的感觉,在线程之间实际上轮换执行。
dos 是单进程 现在的windos 有单核和双核
进程是一个静态的概念, 一个进程中有一个主线程 同一时间点 只有一个 主线程在执行
串行 和 并行
二、Java中的线程
在Java中,“线程”指两件不同的事情:
创建自己的线程对象
new 一个Thread 一个新的线程 就出现了
start 方法 启动一个新的线程
1、java.lang.Thread类的一个实例;
2、线程的执行。
****实现 java.lang.Runnable接口
****继承 Thread类
一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。
Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。
一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。
一旦创建一个新的线程,就产生一个新的调用栈。
public class ThreadTest {
public static void main(String args[]) {
//new 一个线程类对象
Runner1 r = new Runner1();
//r.run(); //方法调用
Thread t = new Thread(r); //多态
t.start();
for(int i = 0; i <= 100; i++) {
System.out.println("Main : " + i);
}
}
}
class Runner1 implements Runnable {
public void run() {
for (int i = 0; i <=100; i++) {
System.out.println("Runner1 : " + i);
}
}
}
执行的结果 就是 : Main 和 Runner 并行输出
线程总体分两类:用户线程和守候线程。
线程调用 必须 调用 start 方法
2. 继承Thread 类
public class ThreadTest {
public static void main(String args[]) {
//new 一个线程类对象
// Runner1 r = new Runner1();
//r.run();
//Thread t = new Thread(r); //多态
// t.start();
Runner2 r = new Runner2();
r.start();
for(int i = 0; i <= 100; i++) {
System.out.println("Main : " + i);
}
}
}
//另外一种形式 extends Thread
class Runner2 extends Thread {
public void run() {
for (int i = 0; i <=100; i++) {
System.out.println("Runner1 : " + i);
}
}
}
能实现接口 尽量不要 extends Thread
二. 方法介绍
sleep 方法: 会抛异常
public class TestInterrupt {
public static void main(String args[]){
MyThread myThread = new MyThread();
myThread.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
myThread.interrupt();
}
}
}
class MyThread extends Thread {
boolean flag = true;
public void run() { // 不可以抛出异常 只能try
while (flag) {
System.out.println("======" + new Date() + "=====");
try {
sleep(1000);
} catch (InterruptedException e) {
return; //线程结束
}
}
}
}
join 方法 : 合并某个线程
public class TestJoin {
public static void main(String args[]) {
MyThread2 myThread2 = new MyThread2("t1");
myThread2.start();
try {
myThread2.join(); //合并 线程 本来 是并行的 合并过来之后 就变成 串行了
} catch (InterruptedException e) {
}
for (int i = 0; i <= 10; i++) {
System.out.println("I am main thread");
}
}
}
class MyThread2 extends Thread {
MyThread2(String s) { //调用父类的构造方法
super(s);
}
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.println("I am " + getName());
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
yield 方法 让出cpu 给其他线程执行的机会 让一下
public class TestYield {
public static void main(String args[]) {
MyThread3 thread3 = new MyThread3("t3");
MyThread3 thread31 = new MyThread3("t4");
thread3.start();
thread31.start();
}
}
class MyThread3 extends Thread {
MyThread3(String s) {
super(s);
}
public void run() {
for(int i = 0; i <= 100; i++) {
System.out.println(getName() + " : " + i);
if (i % 10 == 0) { //让出一回 t3 和 t4 切换
yield();
}
}
}
}
三.线程优先级
public class TestPriority {
public static void main(String args[]) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread t11 = new Thread(t1);
Thread t22 = new Thread(t2);
t11.setPriority(Thread.NORM_PRIORITY + 3); //比正常 多3个级别
t11.start();
t22.start();
}
}
class T1 implements Runnable {
@Override
public void run() {
for(int i = 1; i <= 1000; i++) {
System.out.println("T1======" + i);
}
}
}
class T2 implements Runnable {
@Override
public void run() {
for(int i = 1; i <= 1000; i++) {
System.out.println("T2======" + i);
}
}
}
四 .一些常见问题
1、线程的名字,一个运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。
2、线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。
3、获取当前线程的对象的方法是:Thread.currentThread();
4、在上面的代码中,只能保证:每个线程都将启动,每个线程都将运行直到完成。一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。
5、当线程目标run()方法结束时该线程完成。
6、一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。
7、线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。
众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。
8、尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列的事实。
9、尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。