程序、进程、线程
- 程序:程序本身是静态的,我们开发程序,就是为了完成特定任务、用某种语言编写的一组指令的集合。
- 进程:对进程来说,其就是程序装载入内存,开始运行的状态,是一个动态的概念。而且,进程是资源分配的基本单位,系统在运行的时候,会给进程分配不同的内存区域
- 线程:对于进程来说,其可以划分为好几个线程。线程是操作系统调度和执行的单位,每个线程都有自己的独立运行栈和程序计数器,线程切换开销比较小。一个进程中的多个线程共享同一块内存单元、内存地址空间,也就是说从同一块堆中分配对象,可以访问相同的变量和对象。
线程的创建和使用
对Java来说,其线程的创建方式有数种,这里开始逐条介绍
1、继承Thread来重写run方法
Java中声明一个线程使用Thread
类,这种创建方式需要自己继承Thread
类,然后重写run
方法来执行自己想要的逻辑,最后开启线程必须使用start
方法!
run方法只是此线程被调度时会执行的逻辑。
以下为测试Demo,分别用两个线程打印奇数和偶数:
public class ThreadDemo
{
public static void main(String[] args) {
MyThread1 thread1=new MyThread1();
MyThread2 thread2=new MyThread2();
thread1.start();
thread2.start();
}
}
class MyThread1 extends Thread
{
@Override
public void run() {
for(int i=0;i<100;i++)
{
if(i%2==0)
{
System.out.println("偶数: "+i);
}
}
}
}
class MyThread2 extends Thread
{
@Override
public void run() {
for(int i=0;i<100;i++)
{
if(i%2!=0)
{
System.out.println("奇数: "+i);
}
}
}
}
这里如果开启线程时,start方法调用后会自己调用run方法。如果开始线程使用run方法,那么多线程效果失效。
3、实现Runable接口,重写run方法
class Window implements Runnable{
private int ticket=100;
Object object=new Object();
@Override
public void run() {
while(true)
{
synchronized(object)
{
if(ticket>0)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
ticket--;
}
else
{
break;
}
}
}
}
}
这种方式是比较建议使用的
3、实现Callable接口
不多絮叨,直接上代码
public class ThreadNew
{
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread=new NumThread();
//4.将Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
new Thread(futureTask).start();
try {
//6.如果需要返回值可以使用FutureTask对象的get()方法拿到返回值
Object obj = futureTask.get();
System.out.println("和为:"+obj);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1.创建一个实现了Callable的类
class NumThread implements Callable
{
//2.重写call()方法 其返回值是Object类型
@Override
public Object call() throws Exception
{
int ans=0;
for (int i = 1; i <=100; i++)
{
if(i%2==0)
{
System.out.println(i);
ans+=i;
}
}
return ans;
}
}
学到这里我就要批评一下Java的线程,拿个返回值需要这么多步,C++的原生POIX线程可以直接通过指针参数,拿到一个线程的返回值。
4、使用线程池
为了避免频繁地创建和销毁线程,提供资源利用率,我们可以用线程池技术来实现重复利用线程。
public class ThreadPoolTest
{
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
//executorService.execute(new NumThread1()); //适合Runnable接口
executorService.submit(new NumThread2()); //适合Callable接口
}
}
class NumThread1 implements Runnable
{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if(i%2==0)
{
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumThread2 implements Callable
{
@Override
public Object call() throws Exception {
for (int i = 1; i <= 100; i++) {
if(i%2==0)
{
System.out.println(i);
}
}
return null;
}
}