Java Thread Basic

进程与线程

  • 进程是资源分配的最小单位,线程是CPU调度的最小单位
  • 所有与进程相关的资源,都被记录在PCB中

进程控制块(PCB)

  • 进程是抢占处理机的调度单位,线程属于进程,共享其资源
  • 线程只有堆栈寄存器,程序计数器和TCB组成

  • 进程可被看成独立的应用
  • 进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径
  • 线程没有独立的地址空间,多进程的程序较多线程健壮
  • 进程的切换较线程大

Java中的线程与进程

  • Java对OS进行了封装,包括线程与进程
  • 一个程序对应一个进程,一个进程至少一个线程
  • 一个进程对应一个JVM实例,多个线程共享JVM堆
  • Java采用单线程编程模式,程序会自动创建主线程
  • 主线程可以创建子线程,原则上后于子线程完成执行

start & run

package com.interview.javabasic.thread;

public class ThreadTest {
    private static void attack() {
        System.out.println("Fight");
        System.out.println("Current Thread is : " + Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(){
            public void run(){
                attack();
            }
        };
        System.out.println("current main thread is : " + Thread.currentThread().getName());
        t.start();
        t.join();
        t.start();
    }
}

  • 调用start()方法会调用JVM native并创建新的子线程并启动
  • run()方法只是Thread的一个普通方法调用

Thread & Runnable

  • Thread是一个实现Runnable接口的一个类,使run支持多线程。
  • 因类的单一继承原则,推荐使用Runnable接口

    重写thread run方法

package com.interview.javabasic.thread;

public class MyThread extends Thread {
    private String name;
    public MyThread(String name){
        this.name = name;
    }
    @Override
    public void run(){
        for(int i = 0 ; i < 10 ; i ++){
            System.out.println("Thread start : " + this.name + ",i= " + i);
        }
    }
}
package com.interview.javabasic.thread;

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("Thread1");
        MyThread mt2 = new MyThread("Thread2");
        MyThread mt3 = new MyThread("Thread3");
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

实现Runnable接口

package com.interview.javabasic.thread;

public class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name){
        this.name = name;
    }
    @Override
    public void run(){
        for(int i = 0 ; i < 10 ; i ++){
            System.out.println("Thread start : " + this.name + ",i= " + i);
        }
    }
}
package com.interview.javabasic.thread;

public class RunnableDemo {
    public static void main(String[] args) throws InterruptedException {
        MyRunnable mr1 = new MyRunnable("Runnable1");
        MyRunnable mr2 = new MyRunnable("Runnable2");
        MyRunnable mr3 = new MyRunnable("Runnable3");
        Thread t1 = new Thread(mr1);
        Thread t2 = new Thread(mr2);
        Thread t3 = new Thread(mr3);
        t1.start();
        t2.start();
        t3.start();
    }
}

Run

实现对Run方法传参方式

  • 构造函数传参
  • 成员变量传参
  • 回调函数传参

处理线程返回值

  • 主线程等待法
  • 使用Thread类的join()阻塞当前线程以等待子线程处理完毕,粒度不够细
package com.interview.javabasic.thread;

public class CycleWait implements Runnable{
    private String value;
    public void run() {
        try {
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        value = "we have data now";
    }

    public static void main(String[] args) throws InterruptedException {
        CycleWait cw = new CycleWait();
        Thread t = new Thread(cw);
        t.start();
//        while (cw.value == null){
//            Thread.currentThread().sleep(100);
//        }
        t.join();
        System.out.println("value : " + cw.value);
    }
}
  • 通过Callable接口实现

    1、通过Future Task 获取

package com.interview.javabasic.thread;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception{
        String value="test";
        System.out.println("Ready to work");
        Thread.currentThread().sleep(5000);
        System.out.println("task done");
        return  value;
    }

}
package com.interview.javabasic.thread;

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

public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<String>(new MyCallable());
        new Thread(task).start();
        if(!task.isDone()){
            System.out.println("task has not finished, please wait!");
        }
        System.out.println("task return: " + task.get());

    }
}

​ 2、线程池获取

package com.interview.javabasic.thread;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        Future<String> future = newCachedThreadPool.submit(new MyCallable());
        if(!future.isDone()){
            System.out.println("task has not finished, please wait!");
        }
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally {
            newCachedThreadPool.shutdown();
        }
    }
}
  • 可提交多个Callable并发执行

线程的状态

  • New,新建:创建后尚未启动的状态

  • Runnable,运行:包含Running Ready

  • Waiting,无限期等待:不会被分配CPU时间,需要显式唤醒

    无Timeout参数的Object.wait()

    无Timeout参数的Thread.join()

    LockSupport.park()

  • Timed Waiting,限期等待:一定时间后自动唤醒

    Thread.sleep()

    设置Timeout参数的Object.wait()

    设置Timeout参数的Thread.join()

    LockSupport.parkNanos()

    LockSupport.parkUntil()

  • Blocked,阻塞:等待获取排他锁

  • Terminated,结束:已经结束执行

    结束后不能再执行

Sleep & Wait

  • sleep是Thread中的方法,wait是Object中的方法
  • sleep可在任何方法中使用,wait正能在synchronize方法或块中使用
  • Thread.sleep只会让出cpu,不改变锁状态
  • Object.wait既会让出cpu,也会改变锁状态,释放已被占用的同步资源锁
package com.interview.javabasic.thread;

public class WaitSleepDemo {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread A is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread A get lock");
                        Thread.sleep(20);
                        System.out.println("thread A do wait method");
                        lock.wait();
                        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() {
                System.out.println("thread B is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread B get lock");
                        System.out.println("thread B is sleeping 10 ms");
                        Thread.sleep(10);
                        lock.notifyAll();
                        Thread.yield();
                        Thread.sleep(2000);
                        System.out.println("thread B is done");
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }
}

notify & notifyall

  • 锁池,entrylist

entrylist

  • 等待池,waitset

waitset

  • notifyall

    会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会

  • notify

    随机选取一个在等待池中的线程进入锁池去竞争获取锁的机会

package com.interview.javabasic.thread;

import java.util.logging.Level;
import java.util.logging.Logger;

public class NotificationDemo {
    private volatile boolean go = false;

    public static void main(String args[]) throws InterruptedException {
        final NotificationDemo test = new NotificationDemo();

        Runnable waitTask = new Runnable(){

            @Override
            public void run(){
                try {
                    test.shouldGo();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " finished Execution");
            }
        };

        Runnable notifyTask = new Runnable(){

            @Override
            public void run(){
                test.go();
                System.out.println(Thread.currentThread().getName() + " finished Execution");
            }
        };

        Thread t1 = new Thread(waitTask, "WT1"); //will wait
        Thread t2 = new Thread(waitTask, "WT2"); //will wait
        Thread t3 = new Thread(waitTask, "WT3"); //will wait
        Thread t4 = new Thread(notifyTask,"NT1"); //will notify

        //starting all waiting thread
        t1.start();
        t2.start();
        t3.start();

        //pause to ensure all waiting thread started successfully
        Thread.sleep(200);

        //starting notifying thread
        t4.start();

    }
    /*
     * wait and notify can only be called from synchronized method or bock
     */
    private synchronized void shouldGo() throws InterruptedException {
        while(go != true){
            System.out.println(Thread.currentThread()
                    + " is going to wait on this object");
            wait(); //release lock and reacquires on wakeup
            System.out.println(Thread.currentThread() + " is woken up");
        }
        go = false; //resetting condition
    }

    /*
     * both shouldGo() and go() are locked on current object referenced by "this" keyword
     */
    private synchronized void go() {
        while (go == false){
            System.out.println(Thread.currentThread()
                    + " is going to notify all or one thread waiting on this object");

            go = true; //making condition true for waiting thread
            //notify(); // only one out of three waiting thread WT1, WT2,WT3 will woke up
            notifyAll(); // all waiting thread  WT1, WT2,WT3 will woke up
        }

    }
}

yield

yield

package com.interview.javabasic.thread;

public class YieldDemo {
    public static void main(String[] args) {
        Runnable yieldTask = new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                    if (i == 5) {
                        Thread.yield();
                    }
                }
            }
        };
        Thread t1 = new Thread(yieldTask, "A");
        Thread t2 = new Thread(yieldTask, "B");
        t1.start();
        t2.start();
    }
}

线程的中断

  • stop(),supend(),resume() 太暴力,被弃用
  • 无法清理,立即释放锁,产生问题

  • 使用interrupt(),通知线程应该被中断

    1、如果处于被阻塞状态,立即退出被阻塞状态,抛出InterruptedException

    2、处于活动状态,中断标志置位true,继续活动不受影响

package com.interview.javabasic.thread;

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        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.");
    }
}

线程的转换

transfer

  • Copyrights © 2019-2020 Rex