Java程序员必会的多线程和并发(一)

一.首先来说下进程和线程的区别

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个基本单位,一个进程下可以有多个线程运行。

线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只有必要的资源(程序计数器,一组寄存器和栈),但是他可以与同属于一个进程的其他线程共同享有进程所拥有的全部资源。

打个比方来说,计算机开启打开一个程序可以理解为启动了一个进程,而在程序内部执行各种各样的操作则是由多个线程之间互相协调共同完成的。

二.Java中多线程的实现

首先要明白多线程是为了高效利用有限的内存空间来最大限度的利用CPU完成任务的手段

1.继承Thread类,重写run方法(Thread事实上是实现了Runnable的实例)

/*
 * 实现方法1,继承Thread类,重写run方法
 */
public class Thread1 extends Thread{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {            
                Thread.sleep(100);//当前线程沉睡100毫秒
                //打印当前线程名称
                System.out.println(Thread.currentThread().getName()+"正在执行");
            }catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+"执行结束");
            }                    
    }
    public static void main(String[] args) {
        //创建5个线程
        for (int i = 0; i < 5; i++) {
            Thread1 thread=new Thread1();
            thread.start();//启动线程
        }    
    }
}

上述代码创建了五个实例线程,启动后结果如下:

Thread-2正在执行
Thread-0正在执行
Thread-0执行结束
Thread-4正在执行
Thread-4执行结束
Thread-3正在执行
Thread-1正在执行
Thread-1执行结束
Thread-3执行结束
Thread-2执行结束
 

这个结果并不是唯一绝对的,由于线程启动后时间片的交迭,每个线程执行的时间长短不一致,执行完成的时间戳并不与执行顺序 相关。

2.实现Runnable,同样要重写run方法

public class Thread2 implements Runnable{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {            
                Thread.sleep(100);//当前线程沉睡100毫秒
                //打印当前线程名称
                System.out.println(Thread.currentThread().getName()+"正在执行");
            }catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+"执行结束");
            }                    
    }
    public static void main(String[] args) {
        //创建5个线程
        for (int i = 0; i < 5; i++) {
            Thread1 thread=new Thread1();
            thread.start();//启动线程
        }    
    }

}

代码与方法1类似,结果如下 

Thread-1正在执行
Thread-2正在执行
Thread-0正在执行
Thread-0执行结束
Thread-4正在执行
Thread-4执行结束
Thread-3正在执行
Thread-2执行结束
Thread-1执行结束
Thread-3执行结束
 

3.实现Callable接口,重写call方法,call方法有返回值,返回的是Future对象,再次使用FutureTask来接收返回信息(FutureTask对Future对象进行了封装),在创建FutureTask对象时,提示信息(如下图提示部分)使用第一种方法,传递一个Callable对象

Java程序员必会的多线程和并发(一)_第1张图片

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Thread3 implements Callable {

    @Override
    public String call() throws Exception {  //重写call方法
        try {
            Thread.sleep(100);// 当前线程沉睡100毫秒
            // 打印当前线程名称
            System.out.println(Thread.currentThread().getName() + "正在执行");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "执行结束");
        }
        return Thread.currentThread().getName();// 将当前线程名返回
    }

    public static void main(String[] args) {
        try {
            for (int i = 0; i < 5; i++) {
                Thread3 thread = new Thread3();
                //call方法有返回值,返回的是Future对象,在此使用封装了Future对象的FutureTask
                FutureTask futureTask = new FutureTask<>(thread);   
                new Thread(futureTask).start();
                System.out.println("FutureTask中当前Thread对象:"+futureTask.get());//打印FutureTask中当前线程的返回信息
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

 

结果如下:

Thread-0正在执行
Thread-0执行结束
FutureTask中当前Thread对象:Thread-0
Thread-1正在执行
Thread-1执行结束
FutureTask中当前Thread对象:Thread-1
Thread-2正在执行
Thread-2执行结束
FutureTask中当前Thread对象:Thread-2
Thread-3正在执行
Thread-3执行结束
FutureTask中当前Thread对象:Thread-3
Thread-4正在执行
Thread-4执行结束
FutureTask中当前Thread对象:Thread-4
 

 三.三种方式的对比

由于Java的类只能继承一个类而可以实现很多接口,所以实现Runnable和Callable接口则可以更好地扩展(留了一条可以继承类的后路),当继承Thread时,this指向的就是当前线程即Thread.currentThread

四.面试中可能会问的一些问题(真的有用,亲身经历)

1.run和start区别

run方法只是实现Thread类时需要重写的一个方法,注意,他只是一个方法,就跟你去创建一个类然后写一个方法没什么不同,在执行run时仍在主线程(当前线程)中

而start则是标志着线程的启动,不执行start方法则创建的线程无法执行(在主线程创建了新的线程,执行start则去启动创建的线程,也会执行线程中的run方法)

2.Runnable和Callable区别

Runnable从Jdk1.0就有了,run方法无返回值不能抛出异常

Callable从Jdk1.5引入,call方法可以返回值和抛出异常

3.notify和notifyAll

Notify()只能随机唤醒一个线程,不能具体的去唤醒

NotifyAll()唤醒所有的正在wait的线程并允许它们争夺锁保证了至少有一个线程能继续运行

4.interrupted和isInterrupted区别

interrupted静态方法,检查中断状态后会将中断状态清零

isInterrupted非静态方法,检查中断状态后不会改变中断状态标识

5.yield方法作用?

暂停当前正在执行的线程对象,使其他具有相同优先级的线程执行

6.如何使得线程顺序执行(以后准备专门写一篇来讲这个,暂时一掠而过)

两种方法:join()和newSingleThreadExecutor()--------单线程池(线程池以后一定介绍)

7.wait和sleep区别

Wait方法会释放锁,释放持有的资源

Sleep一直持有锁,不会释放资源

你可能感兴趣的:(java之路,多线程和并发)