------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
基础:
进程:一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元,每个进程
都会有独立的 代码 和 数据空间,进程间切换会有较大的开销,系统运行时 会为每个进程分配不同的内存区域
线程:就是一个进程中独立的控制单元,线程在控制着进程的执行,进程的执行其实就是线程的执行,同一类线程共享代码
和数据空间,每个线程有自己独立运行的栈
一个进程中至少有一个线程 (从 WORD来看 可以把 拼写检查当做一个线程),JVM启动不止一个线程,
还有负责垃圾回收机制的线程,进程没了 线程肯定会消失,线程消失了 进程不一定消失,并且所有的
线程都是在进程的基础之上并发同时(同时运行)。
。Thread 类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是 run() 方法
。Thread 中 start() 的功能就是创建一个线程,并自动调用该线程中的 run(); 方法 直接调用 run(); 不会创建新线程
。run() 方法中出现的异常只能 try{} catch(){}
。执行一个线程实际上就是执行该线程中 run(); 的代码,所以要重写run(),想在线程中执行别的方法,必须把该方法放在 run(); 内部
。一个 Thread 对象只能代表一个线程,一个 Thread 对象只能调用一次 start(); 否则会抛出异常
。X核CPU只能一次处理X个程序,而不是同时处理几个,因为CPU处理速度很快,所以看上去像是在同时处理
。每一条线程的 run() 方法都会在 栈 中开辟一块空间,所以 主线程结束 main() 方法弹栈 并不会影响别的线程的执行,在同一个线程
中的多个方法 会共用一个 栈空间,一个方法先开始 先进栈 运行结束后就会先弹出该栈 ,
。wait() 会释放锁 sleep() 不会释放锁
线程的创建:
方法1---直接继承Thread:
class A extends Thread // 创建多线程通过 extends Thread
{
public void run() //重写 父类 Thread 中的 run 方法,来运行线程,
{
// code1
}
}
public class TestThread
{
public static void main(String[] args)
{
A aa = new A();
aa.start();
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 调用从 Thread类 中继承来的 start 方法,来创建线程,start 会自动执行 A 中的 run(); 然后交替执行 code1 code2
* (假设 code1,code 2 都是循环语句)
* 主线程执行main()里的代码,code1与code2都有可能被先执行,执行次数也不一定,start 与 run(); 连用
*/
// code 2
}
}
方法2---实现Runnable接口:
<span style="font-size:14px;">class MyThread implements Runnable
{
public void run()
{
// code1
}
}
public class TestThread2
{
public static void main(String[] args)
{
MyThread mt = new MyThread();
Thread t = new Thread(mt); // 直接为 Thread 类创建一个对象,开辟一个线程
t.start(); // 创建好线程后,调用 Thread 类里的 start() ,开启线程
//code2
}
}</span>
以上两种方法对比:
Thread 类 与 Runnable 接口 的使用结论:
实现 Runnable 接口比继承 Thread 类 有如下优点:
。适合多个相同程序代码的线程去处理同一个资源 (如例 Test1:)
。可以避免由于单继承带来的局限性
。增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的
所以开发中使用 Runnable 接口更为合适
方法三---通过实现Callable接口来创建Thread线程:
Callable接口(也只有一个方法)定义如下:
public interface Callable<V>
{
V call() throws Exception;
}
步骤1:创建实现Callable接口的类SomeCallable<Integer>(略);
步骤2:创建一个类对象:
Callable<Integer> oneCallable = new SomeCallable<Integer>();
步骤3:由Callable<Integer>创建一个FutureTask<Integer>对象:
FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);
注:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。
步骤4:由FutureTask<Integer>创建一个Thread对象:
Thread oneThread = new Thread(oneTask);
步骤5:启动线程:
oneThread.start();
至此,一个线程就创建完成了。
线程的生命周期:
线程在建立后并不马上执行run方法中的代码,而是处于等待状态。线程处于等待状态时,可以通过Thread类的方法来设置线程不各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。
当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。下面的代码演示了线程的创建、运行和停止三个状态之间的切换,并输出了相应的isAlive返回值。
一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后,可以通过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。
生产者消费者:
class Res
{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable
{
private Res r; // 定义 Res 类的引用,用来接收传入的 Res对象
public Input(Res r)
{
this.r = r;
}
public void run() // 【这里是一条线程要执行的代码】
{
int i = 0;
while(true)
{
synchronized(r) //【同步代码块,如果线程没有被wait(),在while(true)时 该代码块会被一直执行】
{
if(r.flag)
{
try
{
r.wait();
/* 【这里 wait() 之后,当前线程等待状态(停在这里,下次被notify()
时从这里下一条代码处开始执行) ,另一条线程开始执行其 run()】
*/
}
catch(Exception e)
{
}
}
if(i%2==0)
{
r.name = "kingowe";
r.sex = "man";
}
else
{
r.name = "孔超";
r.sex = "男";
}
i++;
r.flag = true;
r.notify();
/*
【唤醒线程池中的一条线程,如果线程池中没有等待的线程,该代码也不会报错,执行完这里,
因为是while(true) 并且为synchronized(r),所以继续执行循环体中的代码,由于r.flag=true,
所以在进入上边的if(r.flag)中,执行 r.wait(),当前线程停在r.wait()处,另一条线程开始执行】
*/
}
}
}
}
class Output implements Runnable
{
private Res r;
public Output(Res r)
{
this.r = r;
}
public void run() // 【这里是另一条线程要执行的代码】
{
while(true)
{
synchronized(r)
{
if(!r.flag)
try
{
r.wait();
}
catch(Exception e)
{
}
System.out.println(r.name+ " " + r.sex);
r.flag = false;
r.notify();
}
}
}
}
public class ThreadIO
{
public static void main(String[] args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
public void run(){
synchronized(b){ // <1> 【先锁 b 再锁 a】
sleep(1000);
synchronized(a){ //<2>
...
}
}
}
线程2:
public void run(){
synchronized(a){ //<3> 【先锁 a 再锁 b】
sleep(1000);
synchronized(b){ // <4>
...
}
}
}
。 对于synchronized持有的对象锁,有可能是根据对象的 equals() 或者 compare() 方法来判断每个 synchronized 持有的对象(锁)是否相同
如果结果相同,那么表示这几个synchronized包含的语句同一时间只能有一条线程来执行(一个synchronized执行完才能执行另一个synchronized)