精华内容
下载资源
问答
  • java多线程三种方式区别 1)实现Runnable接口  定义线程类,实现Runnable接口,重写其public void run(),将此类的对象当做Thread类的构造函数中的参数  所有子线程公用一套run中代码 2)继承Thread...

    java多线程三种方式区别



    1)实现Runnable接口

            定义线程类,实现Runnable接口,重写其public void run(),将此类的对象当做Thread类的构造函数中的参数

           所有子线程公用一套run中代码

    2)继承Thread类

            所有子线程各有一套自己的run代码

    3)使用ExecutorService,Callable,Future实现有返回结果的多线程


    java多线程


    1)进程是资源分配的最小单位,线程是处理机调度执行的最小单位;不同的进程有各自的逻辑地址,能共享物理地址,而同一进程的线程之间可以

            共享部分逻辑地址,即:能共享堆内存,每个线程有自己的线程栈,寄存器,私有数据。

    2)多线程的实现方式有:继承Runnable接口、继承Thread类、使用ExecutorService、Callable、Future实现有返回结果的多线程。

    3)多线程时要处理线程同步问题,使用加锁的方式同时又要避免死锁的产生。


    线程同步方式


    互斥锁、读写锁、条件变量、信号量和令牌


    线程同步加锁的方法


    1)synchronized:修饰方法或修饰代码块


    2)lock对象锁:lock()方法会对Lock实例对象进行加锁,因此所有对该对象调用lock()方法的线程都会被阻塞,直到该Lock对象的unlock()方法被调用。


    wait与sleep区别


    1)wait是Object类的方法,sleep是Thread类的方法

    2)wait时必须有锁且放开,sleep时不一定有锁,有的话不放开

    3)wait要靠别人叫醒或打断,sleep自己醒或被打断





    展开全文
  • 多线程的执行的功能都应该在run()方法中进行定义。但是run()方法是不能直接被调用的,这里涉及到操作系统的资源调度问题。要想要启动多线程必须使用start()方法完成(public void start()). 虽然调用的是start()...

    线程概念

    进程:启动一个程序就是一个进程。
    线程:在一个程序里面,多个事情同步进行,这个事情是由线程来完成

    不使用多线程的效果

    如果我们不使用线程,会怎么样呢?看下面代码

    新建立一个hero类包含英雄的name,血量,攻击力,内置一个攻击方法

    package charactor;
    import java.io.Serializable;
    public class Hero{
        public String name;
        public float hp; 
        public int damage;
        public void attackHero(Hero h) {
            try {
                //为了表示攻击需要时间,每次攻击暂停1000毫秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            h.hp-=damage;
            System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n",name,h.name,h.name,h.hp);
            if(h.isDead())
                System.out.println(h.name +"死了!");
        }
        public boolean isDead() {
            return 0>=hp?true:false;
        }
    }
    

    再编写一个主函数,new三个英雄,我们实现盖伦攻击提莫与盲僧

    package multiplethread;
    import charactor.Hero;
    public class TestThread {
        public static void main(String[] args) { 
            Hero gareen = new Hero();
            gareen.name = "盖伦";
            gareen.hp = 616;
            gareen.damage = 50;
    
            Hero teemo = new Hero();
            teemo.name = "提莫";
            teemo.hp = 300;
            teemo.damage = 30;
                      
            Hero leesin = new Hero();
            leesin.name = "盲僧";
            leesin.hp = 455;
            leesin.damage = 80;
             
            Hero bh = new Hero();
            bh.name = "赏金猎人";
            bh.hp = 500;
            bh.damage = 65;
            
            //盖伦攻击提莫
            while(!teemo.isDead()){
                gareen.attackHero(teemo);
            }
     
            //赏金猎人攻击盲僧
            while(!leesin.isDead()){
                bh.attackHero(leesin);
            }
        }
    }
    

    运行代码,发现只有先攻击完提莫,才能再次攻击盲僧
    在这里插入图片描述
    现在我们想一下,在打游戏的过程中,这种效果有点向回合制游戏,你一下我一下,只有一次攻击结束,才能执行下一次攻击,可是在LOL中,盖伦可以转圈圈开大范围伤害,是可以实现同时攻击提莫与盲僧的,这事就需要用到我们的多线程。

    三种创建多线程的方法

    1. 继承线程类Thread

    使用多线程,就可以做到盖伦在攻击提莫的同时,赏金猎人也在攻击盲僧

    设计一个类KillThread 继承Thread,并且重写run方法

    package multiplethread;
    import charactor.Hero;
    public class KillThread extends Thread{     
        private Hero h1;
        private Hero h2;
        public KillThread(Hero h1, Hero h2){
            this.h1 = h1;
            this.h2 = h2;
        }
        public void run(){
            while(!h2.isDead()){
                h1.attackHero(h2);
            }
        }
    }
    

    启动线程办法: 实例化一个KillThread对象,并且调用其start方法

    package multiplethread;
    import charactor.Hero;
    public class TestThread {
        public static void main(String[] args) { 
            Hero gareen = new Hero();
            gareen.name = "盖伦";
            gareen.hp = 616;
            gareen.damage = 50;
     
            Hero teemo = new Hero();
            teemo.name = "提莫";
            teemo.hp = 300;
            teemo.damage = 30;
             
            Hero bh = new Hero();
            bh.name = "赏金猎人";
            bh.hp = 500;
            bh.damage = 65;
             
            Hero leesin = new Hero();
            leesin.name = "盲僧";
            leesin.hp = 455;
            leesin.damage = 80;
             
            KillThread killThread1 = new KillThread(gareen,teemo);
            killThread1.start();
            KillThread killThread2 = new KillThread(bh,leesin);
            killThread2.start();  
        }   
    }
    

    就可以观察到 赏金猎人攻击盲僧的同时,盖伦也在攻击提莫
    在这里插入图片描述

    在这里插入图片描述
    多线程的执行的功能都应该在run()方法中进行定义。但是run()方法是不能直接被调用的,这里涉及到操作系统的资源调度问题。要想要启动多线程必须使用start()方法完成(public void start()).
    虽然调用的是start()方法,但是最终执行的是run()方法,并且所有的线程是交替执行的。

    在JAVA执行过程之中考虑到对于不同层次的开发者的需求,所以其支持有本地的操作系统函数调用,而这项技术就被称为JNI(java本地接口)技术。利用这项技术可以使用一些操作系统提供的底层函数进行一些特殊处理,而在Thread类里面提供的start0()就表示需要将此方法依赖于不同的操作系统实现。

    *.class在jvm中执行
    任何情况下,只要定义了多线程,多线程的启动永远只有一种方案:Thread类中的start()方法。

    2. 实现Runnable接口

    创建类Battle,实现Runnable接口

    package multiplethread;
    import charactor.Hero;
    public class Battle implements Runnable{
        private Hero h1;
        private Hero h2;
        public Battle(Hero h1, Hero h2){
            this.h1 = h1;
            this.h2 = h2;
        }
        public void run(){
            while(!h2.isDead()){
                h1.attackHero(h2);
            }
        }
    }
    

    启动的时候,首先创建一个Battle对象,然后再根据该battle对象创建一个线程对象,并启动

    package multiplethread;
    import charactor.Hero;
    public class TestThread {
        public static void main(String[] args) {   
            Hero gareen = new Hero();
            gareen.name = "盖伦";
            gareen.hp = 616;
            gareen.damage = 50;
     
            Hero teemo = new Hero();
            teemo.name = "提莫";
            teemo.hp = 300;
            teemo.damage = 30;
             
            Hero bh = new Hero();
            bh.name = "赏金猎人";
            bh.hp = 500;
            bh.damage = 65;
             
            Hero leesin = new Hero();
            leesin.name = "盲僧";
            leesin.hp = 455;
            leesin.damage = 80;
             
            Battle battle1 = new Battle(gareen,teemo);
            new Thread(battle1).start();
            Battle battle2 = new Battle(bh,leesin);
            new Thread(battle2).start();
     
        }
         
    }
    

    battle1 对象实现了Runnable接口,所以有run方法,但是直接调用run方法,并不会启动一个新的线程。
    必须,借助一个线程对象的start()方法,才会启动一个新的线程。
    所以,在创建Thread对象的时候,把battle1作为构造方法的参数传递进去,这个线程启动的时候,就会去执行battle1.run()方法了

    3. 匿名类

    使用匿名类,继承Thread,重写run方法,直接在run方法中写业务代码匿名类的一个好处是可以很方便的访问外部的局部变量。
    前提是外部的局部变量需要被声明为final。(JDK7以后就不需要了)

    package multiplethread;
    import charactor.Hero;
    public class TestThread {
        public static void main(String[] args) {  
            Hero gareen = new Hero();
            gareen.name = "盖伦";
            gareen.hp = 616;
            gareen.damage = 50;
      
            Hero teemo = new Hero();
            teemo.name = "提莫";
            teemo.hp = 300;
            teemo.damage = 30;
              
            Hero bh = new Hero();
            bh.name = "赏金猎人";
            bh.hp = 500;
            bh.damage = 65;
              
            Hero leesin = new Hero();
            leesin.name = "盲僧";
            leesin.hp = 455;
            leesin.damage = 80;
              
            //匿名类
            Thread t1= new Thread(){
                public void run(){
                    //匿名类中用到外部的局部变量teemo,必须把teemo声明为final
                    //但是在JDK7以后,就不是必须加final的了
                    while(!teemo.isDead()){
                        gareen.attackHero(teemo);
                    }              
                }
            };
            t1.start();
            Thread t2= new Thread(){
                public void run(){
                    while(!leesin.isDead()){
                        bh.attackHero(leesin);
                    }              
                }
            };
            t2.start();    
        }   
    }
    

    Thread和Runnable方式的优缺点

    采用继承Thread类方式:

    • 优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
    • 缺点:
    1. 因为线程类已经继承了Thread类,所以不能再继承其他的父类。
    2. 多个线程不能共享同一份资源;

    采用实现Runnable接口方式:

    • 优点:
    1. 线程类只是实现了Runable接口,还可以继承其他的类。
    2. 在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
    • 缺点:
    1. 编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

    start()和run()的区别

    • start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取cpu的执行权才可以执行
    • run()方法是由jvm创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)
    展开全文
  • java多线程其实在工作中接触的并不是很多,偶尔用一下,但是这个特性又是开发工程走向大牛必须要掌握的知识点,所以花几天时间整理了一下,一方便梳理知识点,另一方面也是为了以后更好地使用。 一. 线程和进程 ...

    前言:

    java多线程其实在工作中接触的并不是很多,偶尔用一下,但是这个特性又是开发工程师走向大牛必须要掌握的知识点,所以花几天时间整理了一下,一方便梳理知识点,另一方面也是为了以后更好地使用。

    一. 线程和进程

    线程可以理解是一个程序中可以独立执行的模块,一个程序在一个时间段内同时做好几件事(起码表面看起来是的)就是多线程最明显的表征;

    进程是一次计算机的执行活动,可以是整个程序也可以是部分程序的动态执行;

    从概念上看,进程是包含线程的,一个进程至少包含一个线程。

    区别:

    1. 系统资源管理区别

    进程是在操作系统上运行的,有独立的地址空间;线程是运行在进程内部的,一个进程可以有多个线程;在一个进程内多线程可以交替切换,提高系统的并发度。

    2. 通信行为区别

    进程通过操作系统转发指令,是拥有独立资源的单位;线程不拥有系统资源,只能访问进程的资源;同一个进程的多个线程可以共享这个进程的所有资源;线程拥有自己的栈空间,拥有独立的执行序列。

    3. 系统开销区别

    创建进程时系统需要分配内存区域;在切换进程时需要保存当前进程的CPU环境并且要为被调度运行的进程设置CPU环境;线程创建不需要分配内存,使用的是所属的进程资源;切换线程时也只需要保存和设置少量寄存器的内容,不涉及存储器管理方面操作;线程消耗的资源远小于进程。

    二、线程的生命周期

    1. 线程状态

    新建状态

    可运行状态

    运行状态

    阻塞状态

    结束状态

    三、实现多线程的两个基础方法

    1. 继承Thread重写run方法

    public class MyThread extends Thread {
        private int num;
        private String threadName;
        private long result;
    
        public MyThread(int num, String threadName) {
            this.threadName = threadName;
            this.num = num;
        }
    
        public void run() {
            for (int i = 0; i < num; i++) {
                result += i;
            }
        }
    
        public String getThreadName() {
            return threadName;
        }
    
        public void setResult(long result) {
            this.result = result;
        }
    
        public long getResult() {
            return result;
        }
    }

    2. 实现Runnable 接口重写run方法

    public class MyRunnable implements Runnable {
        private int num;
        private String threadName;
        private long result;
    
        public MyRunnable(int num, String threadName) {
            this.threadName = threadName;
            this.num = num;
        }
    
        public void run() {
            for (int i = 0; i < num; i++) {
                result += i;
            }
        }
    
        public String getThreadName() {
            return threadName;
        }
    
        public void setResult(long result) {
            this.result = result;
        }
    
    
        public long getResult() {
            return result;
        }
    }

    3. 测试两种方法

    package thread;
    
    
    public class Main {
        public static void main(String[] args) {
            threadTest();
    //        runnableTest();
        }
    
        private static void threadTest() {
            MyThread myThread_1 = new MyThread(10, "thread_1");
            MyThread myThread_2 = new MyThread(10000, "thread_2");
            myThread_1.setResult(10);
            myThread_1.start();
            myThread_2.start();
    
            do {
                System.out.println("--------------------------------------------------");
                System.out.println("thread name: " + myThread_1.getThreadName() + ", status:  " + myThread_1.isAlive() + ",result: " + myThread_1.getResult());
                System.out.println("thread name: " + myThread_2.getThreadName() + ", status:  " + myThread_2.isAlive() + ",result: " + myThread_2.getResult());
            } while (myThread_1.isAlive() || myThread_2.isAlive());
        }
    
        private static void runnableTest() {
            MyRunnable myRunnable_1 = new MyRunnable(10, "runnable_1");
            MyRunnable myRunnable_2 = new MyRunnable(10000, "runnable_2");
    
            Thread thread_1 = new Thread(myRunnable_1);
            Thread thread_2 = new Thread(myRunnable_2);
            thread_1.start();
            thread_2.start();
    
            do {
                System.out.println("--------------------------------------------------");
                System.out.println("thread name: " + myRunnable_1.getThreadName() + ", status:  " + thread_1.isAlive() + ",result: " + myRunnable_1.getResult());
                System.out.println("thread name: " + myRunnable_2.getThreadName() + ", status:  " + thread_2.isAlive() + ",result: " + myRunnable_2.getResult());
            } while (thread_1.isAlive() || thread_2.isAlive());
        }
    
    }
    

    4. 两种方法的比较

    如果非要说区别,其实就是实现接口和继承的区别,看开发者的使用习惯

    另外一点,两种方法的run都是不能传参数的,只能通过类的方法设置参数使用

    四、实现Callable接口重写call()方法实现多线程

    上述两种基础方法的run都是void类型,想获取返回只能另加逻辑,而实现Callable接口重写call()方法的好处是允许call函数有返回,下面举例

    import java.util.concurrent.Callable;
    
    public class MyCall implements Callable<Long> {
        private int num;
        private String threadName;
        private long result;
    
        public MyCall(int num, String threadName) {
            this.threadName = threadName;
            this.num = num;
        }
    
        public Long call() throws Exception {
    
            for (int i = 0; i < num; i++) {
                result += i;
            }
            return result;
        }
    }
    
    
    // 下面是测试
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class Main {
        public static void main(String[] args) {
            try {
                callTest();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    
        private static void callTest() throws ExecutionException, InterruptedException {
            MyCall myCall_1 = new MyCall(10, "call_1");
            MyCall myCall_2 = new MyCall(10000, "call_2");
            FutureTask<Long> f1 = new FutureTask<Long>(myCall_1);
            FutureTask<Long> f2 = new FutureTask<Long>(myCall_2);
            Thread thread_1 = new Thread(f1);
            Thread thread_2 = new Thread(f2);
            thread_1.start();
            thread_2.start();
            System.out.println(f1.get());    // 获取返回
            System.out.println(f2.get());    // 获取返回
        }
    
    }

    五、线程池管理多线程

    给一个简单的样例

    import java.util.concurrent.Callable;
    
    public class MyCall implements Callable<Long> {
        private int num;
        private String threadName;
        private long result;
    
        public MyCall(int num, String threadName) {
            this.threadName = threadName;
            this.num = num;
        }
    
        public Long call() throws Exception {
    
            for (int i = 0; i < num; i++) {
                result += i;
            }
            return result;
        }
    }
    
    
    
    // 线程池方式测试
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class ThreadPool {
    
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            MyCall myCall_1 = new MyCall(10, "call_1");
            MyCall myCall_2 = new MyCall(10000, "call_2");
            ExecutorService service = Executors.newFixedThreadPool(5);
            Future<Long> f1 = service.submit(myCall_1);
            Future<Long> f2 = service.submit(myCall_2);
            System.out.println(f1.get());    // 获取返回
            System.out.println(f2.get());    // 获取返回
            service.shutdown();
        }
    }

    六、四种方式总结

    1. 继承Thread和实现Runnable使用起来比较接近,唯一区别就是Runnable避免了单一继承的缺点

    2. 有返回的情况下建议使用Callable,而且可以抛出异常方便定位问题

    3.线程池其实不算是实现方式(有些人会把这个也算是实现方式),它更像是一种管理多线程的方式

     

     

    展开全文
  • 实现多线程三种方式区别? Thread实现多线程是单继承的。 Runnable接口可以解决Thread的单继承问题 Callable接口可以解决Runnable的无返回值问题 注意:get方法是阻塞的,即:线程无返回结果,get方法会一直...

    实现多线程的三种方式的区别?

    • Thread实现多线程是单继承的。
    • Runnable接口可以解决Thread的单继承问题
    • Callable接口可以解决Runnable的无返回值问题
      注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待
      Callable接口实现方式
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    class ThreadA implements Callable<String>{ 
        vate boolean flag=false;//抢答处理
        @Override
        public String call() throws Exception {
            synchronized (this){
                if(this.flag==false){
                    this.flag=true;
                    return Thread.currentThread().getName()+"抢答成功";
                }else {
                    return Thread.currentThread().getName()+"抢答失败";
                }
            }
        }
    }
    public class ThreadDemo6 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ThreadA threadA=new ThreadA();
            FutureTask<String> task=new FutureTask<>(threadA);
            FutureTask<String> task1=new FutureTask<>(threadA);
            FutureTask<String> task2=new FutureTask<>(threadA);
            new Thread(task,"竞赛者A").start();
            new Thread(task1,"竞赛者B").start();
            new Thread(task2,"竞赛者C").start();
            System.out.println(task.get());
            System.out.println(task1.get());
            System.out.println(task2.get());
        }
    }

    Runnable实现方式
    函数式编程实现
    ()->{}等价于new runnable(){}的方式。

    public class ThreadDemo1 {
        public static boolean flag=true;
        public static void main(String[] args) {
            /**
             * 线程的停止方式
             */
            new Thread(()->{
                long num=0;
                while (flag){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在运行、num="+num++);
                }
            },"执行线程").start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag=false;
        }
    }

    Thread实现方式
    thread底层实现

    展开全文
  • 多线程一、前言二、进程和线程2.1进程的介绍2.2线程的介绍2.3进程和线程的关系以及区别三多线程的实现3.1继承Thread类3.2实现Runnable接口3.3实现Callable接口3.4案例:模拟售票员售票3.5实现方式的比较3.6调用...
  • 在JAVA中实现多线程三种方式: 一、继承 Thread 类 二、实现 Runnable 接口 三、继承 Callable类 一、继承 Thread 类 直接继承 Thread 类是最简单的多线程实现方式,你可以根据实际需求新建出多种不同的...
  • 三种方式区别 实现Runnable接口可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被线程共享,代码与数据是独立的;适合个相同程序代码的线程去处理同一资源的情况。 继承Thread类和实现...
  • 方式一:继承Thread类 1.自定义一个类继承Thread类; 2.重写Thread类中的run方法(自定义线程的任务代码就放在run方法里); 3.创建Thread子类的对象,并且调用start方法开启线程,(run方法不能直接调用) ...
  • Java多线程三种实现方式区别

    千次阅读 2020-03-03 14:03:21
    一、什么情况下创建线程不使用线程池? 1、需要定义线程的优先级;...二、创建线程三种方式,以及它们的优缺点。 1、继承Thread类; 2、实现Runable接口; 3、实现Callable接口。 三、三种实现方式区别 ...
  • (1)线程:一个程序同时执行个任务。通常,每一个任务就称为一个线程线程是OS任务执行的基本单位; (2)进程:操作系统中一个程序的执行周期称为一个进程,进程是资源分配的基本单位; (3)线程与进程的...
  • 三种创建线程的方法分别在上述文章中已介绍,分别为:CreateThread,AfxBeginThread,_beginthread/beginthreadex区别: CreateThread是Windows API函数,提供操作系统级别操作,不用于MFC及RTL函数中。一般不建议...
  • Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方 法就是通过 Thread类的 **start()**实例方法。start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法。 ...
  • Java中创建线程主要有三种方式: ①继承thread重写run,调用start package example.testForJava; public class threadTest extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { ...
  • Java多线程实现的三种方式区别

    千次阅读 2019-06-30 16:28:52
    1.1.三种使用线程的方法 实现 Runnable 接口; 实现 Callable 接口; 继承 Thread 类。 实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 ...
  • 1.实现多线程方式有多种: 1.1 继承Thread类 1.2 实现Runnable接口 1.3 实现Callable接口 2.代码实现 2.1 继承java.lang.Thread类 public class ThreadDemo { public static void main(String[] args) { Thread ...
  • Java实现线程三种方式区别 Java实现线程三种方式: 继承Thread 实现Runnable接口 实现Callable接口 区别: 第一种方式继承Thread就不能继承其他类了,后面两种可以; 使用后两种方式可以线程共享一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 773
精华内容 309
关键字:

多线程三种方式区别