Java多线程学习笔记(一)

时间:2023-02-25 11:52:09

一、线程和进程
1、线程是进程的组成部分,一个进程可以有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但是不拥有系统资源。它与父进程的其它线程共享进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便,但是必须更加小心。
2、一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行。
二、并发性和并行性
1、并发性(concurrency):指在同一时刻只能有一条指令执行,但多个进程或线程指令被快速的轮换执行,使得宏观上具有多个进程或线程同时执行的效果。
2、并行性(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。
三、创建线程的方法
1、通过继承Thread类来创建并启动多线程
这种创建方法多个线程之间是不共享数据的,每个线程轮换独立运行。如下面代码,在主线程中创建的两个线程的变量值都是从0加到99.

 public class creat_thread extends Thread
{
private int i;
public void run()
{
for(;i<100;i++)
{ // System.out.println(this.getName()+" "+i);
System.out.println(getName()+" "+i);

}
}
public static void main(String[] args)
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20)
{
new creat_thread().start();
new creat_thread().start();
}
}
}
}

这是一段很有意思的代码,我们可以发现在第7行和第15行用了不同的方法获得当前线程的名字。
在第6行注释的代码与第7行效果是一样的,因为类create_thread继承自Thread,故可以用this.getName()获取,也可以省略this。
但是在15行用this获取就会出现下面错误:
creat_thread.java:15: 错误: 无法从静态上下文中引用非静态变量 this
System.out.println(this.getName()+” “+i);
原因就是main方法是静态的没有创建对象之前就存在的,因此在这里用this表示当前对象就会出现编译错误。
而Thread.currentThread().getName()就是先得到Thread中的当前线程然后获取线程名字,当然了在第7行用该方法也是可行的。
下面是程序输出结果片段:
Java多线程学习笔记(一)
有没有发现一个更有意思的现象?
当主线程计数器累加到22时按理说已经开启另外的线程了,结果其它线程并没有执行run方法,而是主线程累加器变为了23,原因很简单,线程的创建是需要一定时间的,在开启过程中被主线程无情的抢占了哈哈
2、实现Runnable接口来创建线程(共享类中的数据)
Runnable在Java API中是这样描述的:
The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example, Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.In addition, Runnable provides the means for a class to be active while not subclassing Thread. A class that implements Runnable can run without subclassing Thread by instantiating a Thread instance and passing itself in as the target. In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.
Runnable接口应该由任何类实现,其实例旨在由线程执行。 类必须定义一个没有参数的方法run。此接口旨在为希望在活动时执行代码的对象提供公共协议。 例如,Runnable由Thread类实现。 处于活动状态仅仅意味着线程已经启动并且尚未停止。此外,Runnable提供了一个类的方法,使其处于活动状态,而不对Thread进行子类化。 实现Runnable的类可以通过实例化Thread实例并将自身作为目标传递,而无需对Thread进行子类化。 在大多数情况下,如果您只计划覆盖run()方法并且没有其他Thread方法,则应使用Runnable接口。 这很重要,因为类不应该是子类,除非程序员打算修改或增强类的基本行为。
When an object implementing interface Runnable is used to create a thread, starting the thread causes the object’s run method to be called in that separately executing thread.
当一个实现接口Runnable的对象被用来创建一个线程时,启动线程会导致在该单独执行的线程中调用该对象的run方法。
实现代码:

public class creat_thread implements Runnable
{
private int i;
public void run()
{
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);

}
}
public static void main(String[] args)
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20)
{
creat_thread t= new creat_thread();
new Thread(t).start();
new Thread(t).start();
}
}
}
}

要注意的是第7行代码不能再使用this.getName()和getName()方法获取线程名字了,因为create_thread类不再继承自Thread。
执行结果如下:
Java多线程学习笔记(一)
有没有发现执行结果乱糟糟的,run()方法中的变量没有如我们想象的那样从0规律的加到99,这就涉及到共享数据时产生的安全问题。
关于安全问题在下一篇博文中单独分析。