Java线程创建的四种方式总结

多线程的创建,方式一:继承于Thread类

1.创建一个继承于Thread类的子类

2.重写Thread类的run()--->将此线程执行的操作声明在run()中

3.创建Thread类的子类的对象

4.通过此对象调用start():

start()方法的两个作用:

A.启动当前线程

B.调用当前线程的run()

创建过程中的两个问题:

问题一:我们不能通过直接调用run()的方式启动线程

问题二:在启动一个线程,遍历偶数,不可以让已经start()的线程去执行,会报异常;正确的方式是重新创建一个线程的对象。

//1.创建一个继承于Thread类的子类
class MyThread extends Thread{
    //2.重写Thread类的run()
    @Override
    public void run() {//第二个线程
       for(int i = 0;i < 10;i++){
           if(i % 2 == 0){
               System.out.println(i);
           }
       }
    }
}
public class ThreadTest {
    public static void main(String[] args) {//主线程
        //3.创建Thread类的子类的对象
        MyThread t1 = new MyThread();
        //4.通过此对象调用start()
        t1.start();
        //问题一:不能通过直接调用run()的方式启动线程
//        t1.run();//错误的
        //问题二:再启动一个线程:我们需要再创建 一个对象
        //t1.start();//错误的
        MyThread t2 = new MyThread();
        t2.start();

        for(int i = 0;i < 10;i++){
            if(i % 2 != 0){
                System.out.println(i + "****main()******");
            }
        }
    }
}

此代码在主线程内输出奇数,在另一个线程里输出偶数,则输出结果应该是两个输出结果是交互的。

1****main()******
3****main()******
5****main()******
7****main()******
0
2
4
6
8
9****main()******

class Window extends Thread{//创建三个窗口卖票, 总票数为100张,使用继承于Thread类的方式
    private static int ticket = 100;//三个窗口共享:声明为static
    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}
public class WindowTest2 {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
//        MyThread1 m1 = new MyThread1();
//        MyThread2 m2 = new MyThread2();
//        m1.start();
//        m2.start();
        //由于造的类只创建过一次对象,后面就不用了,可以考虑使用匿名类的方式
        //创建Thread类的匿名子类的方式
        new Thread(){
            @Override
            public void run() {
                for(int i = 0;i < 100;i++){
                    if(i % 2 == 0){
                        System.out.println(i);
                    }
                }
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                for(int i = 0;i < 100;i++){
                    if(i % 2 != 0){
                        System.out.println(i);
                    }
                }
            }
        }.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);
            }
        }
    }
}

创建多线程的方式二:实现Runnable接口

  • 创建一个实现了Runnable接口的类
  • 实现类去实现Runnable中的抽象方法:run()
  • 创建实现类的对象
  • 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  • 通过Thread类的对象调用start()
class MThread implements Runnable{
    //2.实现类去实现Runnable中的抽象方法:run()
    @Override
    public void run() {
        for(int i = 0;i < 100;i++){
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        MThread mThread = new MThread();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(mThread);
        t1.setName("线程1");
        //5.通过Thread类的对象调用start():A.启动线程B.调用当前线程的run()-->调用了Runnable类型的target
        t1.start();
        //再启动一个线程,遍历100以内的偶数//只需重新实现步骤4,5即可
        Thread t2 = new Thread(mThread);
        t2.setName("线程2");
        t2.start();
    }
}
class window1 implements Runnable{//创建三个窗口卖票, 总票数为100张,使用实现Runnable接口的方式
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (true){
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                ticket--;
            } else {
                break;
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        window1 w = new window1();//只造了一个对象,所以100张票共享
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);
        t1.setName("线程1");
        t2.setName("线程2");
        t3.setName("线程3");
        t1.start();
        t2.start();
        t3.start();
    }
}

创建线程的方式三:实现Callable接口---JDK5.0新增

与使用Runnable相比,Callable功能更强大些

>相比run()方法,可以有返回值

>方法可以抛出异常

>支持泛型的返回值

>需要借助FutureTask类,比如获取返回结果

Future接口

>可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。

>FutureTask是Futrue接口的唯一的实现类

>FutureTaskb同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

//1.创建一个实现Callable的实现类
class NumThread implements Callable{
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for(int i = 1;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;//sum是int,自动装箱为Integer(Object的子类)
    }
}
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 {
            //获取Callable中call()的返回值(不是必须的步骤)
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

创建线程的方式四:使用线程池--->JDK5.0新增

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

好处:>提高响应速度(减少了创建新线程的时间)

>降低资源消耗(重复利用线程池中线程,不需要每次都创建)

>便于线程管理:A.corePoolSize:核心池的大小 B.maximumPoolSize:最大线程数 C.keepAliveTime:线程没有任务时最多保持多长时间后会终止

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //1.提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();
        //2.执行指定的线程操作。需要提供实现Runnable 接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适用于Runnable
        service.execute(new NumberThread1());//适用于Runnable
//        service.submit(Callable callable);//适用于Callable
        //3.关闭连接池
        service.shutdown();
    }
}

比较创建线程的两种方式:

开发中:优先选择:实现Runnable接口的方式
原因:1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。
系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。---生命周期

线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。

线程作为调度和执行的单位,每个线程拥有独立的运行栈和计数器,每个进程拥有独立的方法区和堆;意味着,多个线程共享一个方法区和堆。而共享的就可以优化,同时,共享的也会带来安全隐患,这就需要我们解决线程安全问题

背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需使用多线程呢?

使用多线程的优点:

1.提高应用程序的响应。对图形化界面更有意义,可增强用户体验。

2.提高计算机系统CPU的利用率

3.改善程序结构。将即长又复杂的线程分为多个线程,独立运行,利于理解和修改

何时需要多线程

1.程序需要同时执行两个或多个任务。

2.程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。

3.需要一些后台运行的程序时。

public class Sample{
    public void method1(String str){
        System.out.println(str);
    }
    public void method2(String str){
        method1(str);
    }
    public static void main(String[] args){
        Sample s = new Sample();
        s.method2("hello!");
    }
}

注意:此程序不是多线程!main方法中调用了method1,method1中又调用了method2;是一条执行路径,所以是单线程

测试Thread类的常用方法:

1.start():启动当前线程:调用当前线程的run()
2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前CPU的执行权(下一刻CPU执行的线程仍是随机的)
>暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
>若队列中没有同优先级的线程,忽略此方法
7.join():在线程a中调用线程b的join(),此时,线程a就进入阻塞状态(停止执行),直到线程b完全执行完以后,线程b才结束阻塞状态(开始执行)。
8.sleep(long millitime):让当前线程"睡眠"指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。会抛出InterruptedException异常
* 9.isAlive():判断当前线程是否存活

class HelloThread extends Thread{
    @Override
    public void run() {
        for(int i = 0;i < 100;i++){
            if(i % 2 != 0){
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" +Thread.currentThread().getPriority() + ":" + i);
            }
        }
    }
    public HelloThread(String name){
        super(name);
    }
}
public class ThreadMethodTest {
    public static void main(String[] args) {
        HelloThread h1 = new HelloThread("Thread:1");//通过构造器给线程命名,但前期是得在子类中提供一个构造器
//        h1.setName("线程一");
        //设置分线程的优先级
        h1.setPriority(Thread.MAX_PRIORITY);
        h1.start();
        //给主线程命名
        Thread.currentThread().setName("主线程");
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        for(int i = 0;i < 100;i++){
            if(i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
            }
//            if(i == 20){
//                try {
//                    h1.join();//join()的测试
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
        }
    }
}

线程的优先级:

1.MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5--->默认优先级
2.如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下,不一定被执行,并不意味着只有当高优先级的线程执行完毕后,低优先级的线程才执行。

到此这篇关于Java线程创建的四种方式总结的文章就介绍到这了,更多相关Java 线程创建内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-09-14

Java线程创建(卖票),线程同步(卖包子)的实现示例

1.线程两种创建方式:new Thread(new Runnable() {}) 如下FileOutputStream源码中抛出异常,为了让写代码人自己写try catch异常提示信息. package com.itheim07.thread; /* * 进程和线程 * 1. 进程 : 航空母舰(资源: 燃油 弹药) * 2. 线程 : 舰载机 * 一个软件运行: 一个军事活动, 必须有一艘航母出去,但执行具体任务的是航母上的舰载机 * 一个软件运行,至少一个进程, 一个进程中至少一个线程.谷歌

java中线程池最实用的创建与关闭指南

目录 前言 线程池创建 只需要执行shutdown就可以优雅关闭 执行shutdownNow关闭的测试 总结 前言 在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等. 线程池创建 避免使用Executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用ThreadPoolExecutor的构造函数来自己创建线程池.在创建的同时,给BlockQueue指定容量就可以了. private stat

java 线程创建多线程详解

Java 线程类也是一个 object 类,它的实例都继承自 java.lang.Thread 或其子类. 可以用如下方式用 java 中创建一个线程,执行该线程可以调用该线程的 start()方法: Tread thread = new Thread(); thread.start(); 在上面的例子中,我们并没有为线程编写运行代码,因此调用该方法后线程就终止了. 编写线程运行时执行的代码有两种方式:一种是创建 Thread 子类的一个实例并重写 run 方法,第二种是创建类的时候实现 Run

java多线程创建及线程安全详解

什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位.每个程序程序都至少有一个线程,也即是程序本身. 线程的状态 新建(New):创建后尚未启动的线程处于这种状态 运行(Runable):Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间. 等待(Wating):处于这种状态的线程不会被分配CPU执行时间.等待状态又分为无限期等待和有限期等待,处于无

java多线程关键字final和static详解

这篇文章主要介绍了java多线程关键字final和static详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 final关键字 1.final关键字在单线程中的特点: 1)final修饰的静态成员:必须在进行显示初始化或静态代码块赋值,并且仅能赋值一次. 2)final修饰的类成员变量,可以在三个地方进行赋值:显示初始化.构造代码块和构造方法,并且仅能赋值一次. 3)final修饰的局部变量,必须在使用之前进行显示初始化(并不一定要在定义是

Java多线程中ReentrantLock与Condition详解

一.ReentrantLock类 1.1什么是reentrantlock java.util.concurrent.lock中的Lock框架是锁定的一个抽象,它允许把锁定的实现作为Java类,而不是作为语言的特性来实现.这就为Lock的多种实现留下了空间,各种实现可能有不同的调度算法.性能特性或者锁定语义.ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,但是添加了类似锁投票.定时锁等候和可中断锁等候的一些特性.此外,它还提供了在激烈争用情况下更

java多线程编程之管道通信详解

上一章节讲了wait/notify通信,这一节我们来探讨使用管道进行通信. java中提供了IO流使我们很方便的对数据进行操作,pipeStream是一种特殊的流,用于不同线程间直接传送数据.一个线程将数据发送到输出管道,另一个线程从输入管道读取数据.通过管道实现通信不需要借助临时文件这类东西. java中提供了四个类使得线程间可以通信: ①字节流:PipeInputStream,PipedOutputStream ②字符流:PipedReader,PipedWriter 下面我们看看字节流的实

java多线程编程之Synchronized关键字详解

本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章  一.分析 synchronized可以修饰实例方法,如下形式: public class MyObject { synchronized public void methodA() { //do something.... } 这里,synchronized 关键字锁住的是当前对象.这也是称为对象锁的原因. 为啥锁住当

Java多线程模式之Balking模式详解

本文实例讲述了Java多线程模式之Balking模式.分享给大家供大家参考,具体如下: 当现在不适合这个操作,或是没有必要进行这个操作时就直接放弃这个操作而回去.这个就是Balking模式 例如王某在餐厅吃饭,当王某需要点餐时喊服务员需要点餐.当服务员A和B都注意到了王某点餐的示意,这时服务员B看到服务员A已经去响应了王某的点餐请求,所以服务员B就不会再过去响应王某的点餐请求. 程序示例: 程序的需求是模拟一个自动保存的功能.自动保存是为了预防计算机忽然断电或则软件突然出错的危险,定期将数据保存

Java多线程连续打印abc实现方法详解

一道编程题如下: 实例化三个线程,一个线程打印a,一个线程打印b,一个线程打印c,三个线程同时执行,要求打印出10个连着的abc. 题目分析: 通过题意我们可以得出,本题需要我们使用三个线程,三个线程分别会打印6次字符,关键是如何保证顺序一定是abc...呢.所以此题需要同步机制来解决问题! 令打印字符A的线程为ThreadA,打印B的ThreadB,打印C的为ThreadC.问题为三线程间的同步唤醒操作,主要的目的就是使程序按ThreadA->ThreadB->ThreadC->Thr

JAVA 多线程之信号量(Semaphore)实例详解

java Semaphore 简介 信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确.合理的使用公共资源. 一个计数信号量.从概念上讲,信号量维护了一个许可集.如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可.每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者.但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动.拿到信号量的线程可以进入

java 多线程交通信号灯模拟过程详解

这学期我们java课程的课程设计项目----交通信号灯的线程设计 实验目的:多线程设计,同步机制 题意 设计一个交通信号灯类: 变量:位置.颜色(红.黄.绿).显示时间(秒). 方法:切换信号灯. 创建并启动两个线程(东西向.南北向)同时运行. 实验要求 设计线程. 设计路口信号灯示意图界面. 进一步将每个方向的信号灯分成3种车道灯:左转.直行和右转. 根据车流量进行时间的模糊控制. 在课程设计的开始并没有仔细看老师的要求,只知道是交通信号灯.然后就开始各种找资料,百度,网上大量的关于红绿灯的设

Java多线程工具篇BlockingQueue的详解

前言: 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全"传输"数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利.本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景. 认识BlockingQueue 阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中通过一个共享的队列,可以使得数据由队列的一端输入,从另外一端输出: 常用的队列主要有以下两种:(当

Java 高并发三:Java内存模型和线程安全详解

网上很多资料在描述Java内存模型的时候,都会介绍有一个主存,然后每个工作线程有自己的工作内存.数据在主存中会有一份,在工作内存中也有一份.工作内存和主存之间会有各种原子操作去进行同步. 下图来源于这篇Blog 但是由于Java版本的不断演变,内存模型也进行了改变.本文只讲述Java内存模型的一些特性,无论是新的内存模型还是旧的内存模型,在明白了这些特性以后,看起来也会更加清晰. 1. 原子性 原子性是指一个操作是不可中断的.即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰