前言:多线程下程序运行的结果是随机的,以下案例代码的运行结果仅供参考
一 通过继承Thread线程创建的方法与执行的步骤
/*
1 继承Thread 2重写run方法 3创建线程继承类的子类
4 调用start(一个线程只能通过调用一次start来进行启动)
*/
class MyThread extends Thread{
public MyThread(String name){
//调用Thread类中的构造方法才能顺序初始化成功线程名
super(name);
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
//线程名(默认):Thread-0,currentThread是获取当前的线程
System.out.println(Thread.currentThread().getName() + i);
}
}
}
public class ThreadTest {
/*
当前主线程是main线程,我们在此又开了个名为myThread的线程,main线程与thread线程的执行时顺序是随机的
由于myThread的启动和被分配cpu时间片都需要时间,所以通常是main线程先执行
只有start才能启动一个线程,启动后需要抢占cpu资源,抢占成功才能执行线程中的run方法,如果直接调用线程的run方法
相当于在主线程main中正常的调用一个对象myThread的run方法
若没有手动设置线程名称时,会自动分配一个线程的名称,如线程对象myThread会被自动分配线程名称Thread-0
设置线程名称是为了区分当前正在执行的线程是哪一个线程,所以在设置线程名称时应避免重复!
*/
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread("myThread");
//start方法是由新建态=> 就绪态。有两步①启动线程②调用线程的run方法
myThread.start();
System.out.println("========以下是main线程的执行步骤========");
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
二 通过实现Runnble的方法
/*
①创建一个Runnable的实现类②实现类去重写接口的run方法③创建实现类的对象
④将该对象作为参数传入到Thread构造器来构造一个Thread类型的对象⑤通过该thread对象调用时start
*/
class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
//线程名(默认):Thread-0,currentThread是获取当前的线程
System.out.println(Thread.currentThread().getName() + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRun());
thread.setName("mythread");
thread.start();
System.out.println("========以下是main线程的执行步骤========");
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
三、实现Callable的方式
方法三比方法二好,原因①call方法可以有返回值 ②Callable支持泛型③call可以抛出异常信息而run中不能
/*
相比较Runable接口,call()方法新增捕获和抛出异常和返回值的功能,实现步骤如下:
①创建一个Callable的实现类并重写call方法,将线程需要执行的任务写到call方法中
②实例化一个该实现类的对象callable
③将callable作为参数构造一个FutureTask的对象ftt
④将ftt作为参数(Runnable接口类型的引用target)构造Thread对象thread
⑤thread.start()启动线程
补充:call的返回值类型即为Callable的泛型类型,通过ftt.get()可以返回call方法的返回值
*/
class MyCall implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ":" +i);
}
return 1;
}
}
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer> callable = new MyCall();
FutureTask<Integer> ftt = new FutureTask<Integer>(callable);
Thread thread = new Thread(ftt);
thread.start();
System.out.println(ftt.get());
}
}
四、通过线程池的方式
由于线程的创建与销毁都是很耗费资源的工作,在高并发的情况下对性能影响非常大,解决:可以提前创建多个线程放入线程池,使用的时不是现造而是去线程池中取,使用完不是销毁而是重新放回线程池,避免频繁的创建于销毁,实现重复利用,类似于公交车,优势非常明显,降低响应时间(节约了创建线程的时间)节约系统资源(重复利用线程池的线程减少了创建和销毁线程的频率)提高系统系统,利于管理(例如可以通过参数指定线程池大小,池内最大线程数,线程保持时间)
class MyRun implements Runnable{
@Override
public void run() {
System.out.println("这是MyRun");
}
}
class MyCall implements Callable{
@Override
public Object call() throws Exception {
return 1;
}
}
/*
1 创建一个指定线程数量的线程池对象service
2 将service强转为ThreadPoolExecutor以便设置线程池的属性
3 需要传入Runnable或Callable接口实现类的对象(匿名对象)作为参数执行指定的线程的操作
4 关闭连接池
*/
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//1 创建一个指定线程数量的线程池对象service
ExecutorService service = Executors.newFixedThreadPool(10);
//2 将service强转为ThreadPoolExecutor以便设置线程池的属性
ThreadPoolExecutor executor = (ThreadPoolExecutor)service;
//3 需要传入Runnable或Callable接口实现类的对象(匿名对象)作为参数执行指定的线程的操作
service.execute(new MyRun());
service.submit(new MyCall());
//4 关闭连接池
service.shutdown();
}
}
推荐学习:线程的安全问题与三种解决方法