线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。只能使用分配给程序的资源和环境。
有关线程的一些特性:
1.Java中如果我们没有自己产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来运行的。
2.线程一旦运行起来,就无法控制它了。
2.进程就是执行中的程序(程序是静态的概念,进程是动态的概念);一个进程可以包含多个线程,但至少包含一个线程
3.不能依靠线程的优先级来决定线程的执行顺序。执行顺序还是取决于底层的操作系统。
4.对于单核CPU来说,某一时刻只能有一个线程来执行(微观串行);从宏观角度来看,多个线程在同时执行(宏观并行)
5.对于双核或者双核以上的CPU来说,可以做到真正的微观串行
Java中如何创建线程?方法有两种
1.自定义类继承Thread类,并重写其中的run方法
2.通过定义实现Runnable接口的类进而实现run方法
注意:上述两种方式创建线程,都是将我们希望线程执行的代码放到run方法中,然后通过调用start()方法开启线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。单独通过Thread对象调用run方法并不会开启线程,仅仅是普通的方法调用。
两种方式创建线程的示例
/**
* 方式一:继承Thread类并重写run方法
*/
public class ThreadTest {
public static void main(String[] args){
Thread t1 = new Thread1("Dream");
Thread t2 = new Thread2();
//调用start方法开启线程
t1.start();
t2.start();
}
}
class Thread1 extends Thread{
public Thread1(String name){
super(name);
}
public void run(){
for(int i=0;i<100;i++){
System.out.println("Hello world:"+i);
}
}
}
class Thread2 extends Thread{
public void run(){
for(int i=0;i<100;i++){
System.out.println("Welcome:"+i);
}
}
}
/**
* 方式二:实现Runnable接口,并实现run方法
*/
public class ThreadTest2 {
public static void main(String[] args){
//如果不传递线程的名字,则线程名字为Thread-number
//其中number是自动增加的,并被所有的Thread所共享(static成员变量)
Thread t1 = new Thread(new MyThread1(),"Dream");
Thread t2 = new Thread(new MyThread2());
//获得线程的名字
System.out.println(t1.getName());
System.out.println(t2.getName());
t1.start();
t2.start();
}
}
class MyThread1 implements Runnable{
public void run(){
for(int i=0;i<100;i++){
System.out.println("welcome:"+i);
}
}
}
class MyThread2 implements Runnable{
public void run(){
for(int i=0;i<100;i++){
System.out.println("hello world:"+i);
}
}
}
Thread类底层代码剖析
Thread类也实现了Runnable接口,因此也实现了Runnable接口中的run方法,代码如下:
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
在第二种方式中,我们在new一个Thread对象的时候,传递了一个实现Runnable接口的类的对象。
方法的注释中写道:如果在我们使用方式一来生成线程对象时,此时并没有传递任何对象的引用及其他参数,那么run方法是什么也不做的,所以我们需要重写run方法。但是当我们使用方式二时,通过new Thread(new MyThread())生成线程对象(上述代码中的target是Runnable类型的),其中new MyThread()生成的对象传给target,这时候调用run方法,实质上是调用了我们自定义的实现Runnable接口中自己实现的run方法
关于局部变量和成员变量
- 成员变量
多个线程对同一对象的成员变量进行操作时,他们对该成员是彼此影响的(一个线程对成员变量的改变会影响另一个线程)
- 局部变量
每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变是不会影响到其他线程的
public class ThreadTest3 {
public static void main(String[] args){
Runnable r = new MyRunnable();
//同一对象r
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable{
private int i;
@Override
public void run() {
//int i = 0;
while(true){
System.out.println("The current num:"+i++);
try {
Thread.sleep((long) Math.random()*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if( 20 == i){
break;
}
}
}
}
输出结果20个;当把private成员变量注释掉,方法run内int i = 0;的注释去掉,那么就会打印40个
线程的消亡:不能使用Thread类的stop方法来终止线程的执行。
一般设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。
class TheThread implements Runnable{
private boolean flag = true;
public void run(){
while(flag){
//线程要执行的代码
}
}
/**
* 停止线程的执行,通过设置标志位
*/
public void stopRunning(){
flag = false;
}
}
class ThreadControl{
private TheThread r = new TheThread();
private Thread t = new Thread(r);
public void startThread(){
t.start();
}
public void stopThread(){
r.stopRunning();
}
}