java

Java--多线程

2019-04-28  本文已影响0人  二妹是只猫
进程和线程的区别
Java进程和线程的关系
线程run()和start()的区别

start()
调用start()方法会创建一个新的子线程并启动,处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行相应线程的run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。start()不能被重复调用

run()
方法只是Thread的一个普通方法,可以被重复调用。如果直接调用run方法,并不会启动新线程!程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的

代码实例证明上面的结论

  private void runCurrentThread(){
      System.out.println("currentThread=="+Thread.currentThread().getName());
      System.out.println("over");
  }
  @Test
  public void diffRunAndStart(){
      Thread thread = new Thread(){
          @Override
          public void run() {
              super.run();
              runCurrentThread();
          }
      };
      System.out.println("current main Thread=="+Thread.currentThread().getName());
      thread.start();
  }

输出结果:

current main Thread==main
currentThread==Thread-0
over

Process finished with exit code 0

将thread.start()换为thread.run(),输出结果:

current main Thread==main
currentThread==main
over

Process finished with exit code 0

以上代码证明我们的结论,下面通过源码来查看它的实现:

    public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    private native void start0();

最终调用了native方法start0(),查看jdk源码:

jdk源码--java.lang.Thread

start0调用了JVM_StartThread,在去看看jvm文件:

jvm.cpp
代码很多,但最终看到创建了一个新的线程,也证实了我们的结论。start()新创建线程并启动
Thead与Runnable的关系

网上有一个结论,用Runnable就可以实现资源共享,而 Thread 不可以。用来论证的代码大概是这样

    @Test
    public void thread(){
        Thread thread1 = new MyThread("thread1");
        Thread thread2 = new MyThread("thread2");
        thread1.start();
        thread2.start();
    }
    class MyThread extends Thread{
        public String name;
        public int tickit = 10;
        public MyThread(String name){
            this.name = name;
        }

        @Override
        public void run() {
            super.run();
            for(int i=0;i<200;i++){
                if (0<tickit){
                    System.out.println(name+" 票号=="+tickit--);
                }
            }
        }
    }

输出:

thread2 票号==10
thread2 票号==9
thread2 票号==8
thread2 票号==7
thread2 票号==6
thread2 票号==5
thread2 票号==4
thread1 票号==10
thread2 票号==3
thread2 票号==2
thread2 票号==1
thread1 票号==9
thread1 票号==8
thread1 票号==7
thread1 票号==6
thread1 票号==5
thread1 票号==4
thread1 票号==3
thread1 票号==2
thread1 票号==1

Process finished with exit code 0
    @Test
    public void runnable(){
        Runnable myRunable = new MyRunnable();
        Thread thread1 = new Thread(myRunable,"thread1");
        Thread thread2 = new Thread(myRunable,"thread2");
        thread1.start();
        thread2.start();
    }
    class MyRunnable implements Runnable{
        public int tickit = 10;

        @Override
        public void run() {
            for(int i=0;i<200;i++){
                if (tickit>0){
                    System.out.println(Thread.currentThread().getName()+" 票号=="+tickit--);
                }
            }
        }
    }

输出:

thread1 票号==10
thread2 票号==9
thread2 票号==7
thread2 票号==6
thread2 票号==5
thread2 票号==4
thread2 票号==3
thread2 票号==2
thread2 票号==1
thread1 票号==8

Process finished with exit code 0

输出貌似验证了这个结论,其实仔细看看代码就知道,这只是两种写法的区别,根本就不是 implements Runnable 与 extends Thread 的区别。
修改代码,在此证明错误:

 @Test
    public void thread(){
        Thread thread1 = new MyThread("thread1");
        Thread thread2 = new MyThread("thread2");
        thread1.start();
        thread2.start();
    }
    static class MyThread extends Thread{
        public String name;
        public static int tickit = 10;
        public MyThread(String name){
            this.name = name;
        }

        @Override
        public void run() {
            super.run();
            for(int i=0;i<200;i++){
                if (0<tickit){
                    System.out.println(name+" 票号=="+tickit--);
                }
            }
        }
    }

输出:

thread2 票号==10
thread2 票号==8
thread2 票号==7
thread1 票号==9
thread1 票号==5
thread1 票号==4
thread1 票号==3
thread2 票号==6
thread2 票号==1
thread1 票号==2

Process finished with exit code 0
由此Runnable相比Thread的优势:
如何给run()方法传参

如何实现处理线程的返回值

1.主线程等待法
    class WaiRunnable implements Runnable{
        public String value ;
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
                value = "it's Lillard time";
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    @Test
    public void mainWait() throws InterruptedException {
        WaiRunnable waitRunnable = new WaiRunnable();
        Thread t = new Thread(waitRunnable);
        t.start();
        //等待value赋值
        while (null==waitRunnable.value){
            Thread.sleep(1000);
        }
        System.out.println("value=="+waitRunnable.value);
    }
value==it's Lillard time

Process finished with exit code 0

缺点:

2.使用Thread类的join()阻塞当前线程以等待子线程处理完毕

只需将上方的while循环改为t.join();

    @Test
    public void mainWait() throws InterruptedException {
        WaiRunnable waitRunnable = new WaiRunnable();
        Thread t = new Thread(waitRunnable);
        t.start();
        t.join();
        System.out.println("value=="+waitRunnable.value);
    }
value==it's Lillard time

Process finished with exit code 0

优点:

缺点:

3.通过Callable接口实现:通过FutureTask或线程池获取

FutureTask:

    class MyCallable implements Callable<String>{
        @Override
        public String call() throws Exception {
            System.out.println("ready");
            Thread.sleep(3000);
            return "it's Lillard time";
        }
    }

    @Test
    public void callable() throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new MyCallable());
        new Thread(futureTask).start();
        if (!futureTask.isDone()) {
            System.out.println("task not finish,please wait");
        }
        System.out.println("task return:"+futureTask.get());
    }
task not finish,please wait
ready
task return:it's Lillard time

Process finished with exit code 0

线程池:

    @Test
    public void executor(){
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future future = executorService.submit(new MyCallable());
        if (!future.isDone()) {
            System.out.println("task not finish,please wait");
        }
        try {
            System.out.println("task return:"+future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            executorService.shutdown();
        }
    }
task not finish,please wait
ready
task return:it's Lillard time

Process finished with exit code 0

Thread的join方法

join() method suspends the execution of the calling thread until the object called finishes its execution.

t.join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续;通常用于在main()主线程内,等待子线程完成再结束main()主线程

    System.out.println("MainThread run start.");

    //启动一个子线程
    Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("threadA run start.");
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("threadA run finished.");
        }
    });
    threadA.start();

    System.out.println("MainThread join before");
    try {
        threadA.join();    //调用join()
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("MainThread run finished.");

MainThread run start. 
threadA run start. 
MainThread join before 
threadA run finished. 
MainThread run finished.

sleep和wait的区别(两者都能使线程进行等待状态)

基本的差别
最重要的本质区别

代码实例:

@Test
    public void diffSleepAndWait() {
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
1               System.out.println("Thread A is waiting to get lock");
                synchronized (lock) {
                    try {
2                       System.out.println("Thread A get lock");
                        Thread.sleep(20);
3                       System.out.println("Thread A do wati");
                        lock.wait(1000);
4                       System.out.println("Thread A is done");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
5               System.out.println("Thread B is waiting to get lock");
                synchronized (lock) {
                    try {
6                       System.out.println("Thread B get lock");
7                       System.out.println("Thread B is sleeping 10 ms");
                        Thread.sleep(10);
8                       System.out.println("Thread B is done");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
Thread A is waiting to get lock
Thread A get lock
Thread B is waiting to get lock
Thread A do wati
Thread B get lock
Thread B is sleeping 10 ms
Thread B is done
Thread A is done

Process finished with exit code 0

开启线程A并上锁(输出1.2),休眠20毫秒这样只休眠了10毫秒的主线程就会启动线程B(输出5,但由于现场A此时占有对象锁,所以不回往下执行),线程A的20秒休眠结束(输出3)调用wait休眠1秒,按照上方关于wait的理论此时释放线程A释放对象锁,线程B继续执行--输出67(Thread.sleep(10))只休眠10毫秒远比线程A的一秒短--输出8,最好线程A休眠时间到--输出4

将Thread A 标记3的wait改为sleep,然后运行同样也可以证明sleep只会让出cpu不回释放锁的结论

线程的状态

notify和notifyall的区别(都能唤醒等待状态的线程)

两个概念

yield

A hint to the scheduler that the current thread is willing to yield
its current use of a processor. The scheduler is free to ignore this
hint

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for(int i= 0;i<6;i++){
                    System.out.println(Thread.currentThread().getName()+"  "+i);
                    if (i==2){
                        Thread.yield();
                    }
                }
            }
        };
        Thread threadA = new Thread(runnable,"A");
        Thread threadB = new Thread(runnable,"B");
        threadA.start();
        threadB.start();
A  0
A  1
A  2
B  0
B  1
B  2
A  3
A  4
A  5
B  3
B  4
B  5

Process finished with exit code 0

输出结果证实了结论,但由于线程调度器可能会忽略这个提示,所以上面这个输出不是100%复现

如何中断线程

已经被抛弃的方法

目前使用的方法
调用interrupt(),通知线程应该中断了:

coding:

  Runnable interruptTask = new Runnable() {
      @Override
      public void run() {
          int i = 0;
          try {
              //在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
              while (!Thread.currentThread().isInterrupted()) {
                  Thread.sleep(100); // 休眠100ms
                  i++;
                  System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") loop " + i);
              }
          } catch (InterruptedException e) {
              //在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
              System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") catch InterruptedException.");
          }
      }
  };
      Thread t1 = new Thread(interruptTask, "t1");
      System.out.println(t1.getName() +" ("+t1.getState()+") is new.");

      t1.start();                      // 启动“线程t1”
      System.out.println(t1.getName() +" ("+t1.getState()+") is started.");

  // 主线程休眠300ms,然后主线程给t1发“中断”指令。
      Thread.sleep(300);
      t1.interrupt();
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");

  // 主线程休眠300ms,然后查看t1的状态。
      Thread.sleep(300);
      System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (TERMINATED) is interrupted now.

Process finished with exit code 0

由于intterrupt不会真的停止线程,所以需要检查中断标志来进行中断处理

上一篇 下一篇

猜你喜欢

热点阅读