Java线程

Java线程的实现方式
方式1:使用 Thread类或继承Thread类
// 创建线程对象
Thread t = new Thread() {
    public void run() {
    // 要执行的任务
    }
};
// 启动线程
方式2:实现 Runnable 接口配合Thread
把【线程】和【任务】(要执行的代码)分开
Thread 代表线程
Runnable 可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {
    public void run(){
    // 要执行的任务
    }
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
方式3:使用有返回值的 Callable 
class CallableTask implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return new Random().nextInt();
    }
}
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用 Future提交返回结果
方式4:使用 lambda
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
Java线程的生命周期
Java 语言中线程共有六种状态,分别是:

NEW(初始化状态)
RUNNABLE(可运行状态+运行状态)
BLOCKED(阻塞状态)
WAITING(无时限等待)
TIMED_WAITING(有时限等待)
TERMINATED(终止状态)
在操作系统层面,Java 线程中的 BLOCKED、WAITING、TIMED_WAITING 是一种状态,即前面我们提到的休眠状态。也就是说只要 Java 线程处于这三种状态之一,那么这个线程就永远没有 CPU 的使用权。

Thread常用方法
sleep方法
调用 sleep 会让当前线程从 Running 进入TIMED_WAITING状态,不会释放对象锁
其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException,并且会清除中断标志
睡眠结束后的线程未必会立刻得到执行
sleep当传入参数为0时,和yield相同
yeld方法
yield会释放CPU资源,让当前线程从 Running 进入 Runnable状态,让优先级更高(至少是相同)的线程获得执行机会,不会释放对象锁;
假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程;
具体的实现依赖于操作系统的任务调度器
join方法    
等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景
public class ThreadJoinDemo {

    public static void main(String[] sure) throws InterruptedException {

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t begin");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t finished");
            }
        });
        long start = System.currentTimeMillis();
        t.start();
        //主线程等待线程t执行完成
        t.join();

        System.out.println("执行时间:" + (System.currentTimeMillis() - start));
        System.out.println("Main finished");
    }
Java线程的中断机制
Java没有提供一种安全、直接的方法来停止某个线程,而是提供了中断机制。中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。被中断的线程拥有完全的自主权,它
既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。
API的使用
interrupt(): 将线程的中断标志位设置为true,不会停止线程 isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位 Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle
public class ThreadInterruptTest {

    static int i = 0;

    public static void main(String[] args)  {
        System.out.println("begin");
        Thread t1 = new Thread(new Runnable() {
            @Override
            public  void run() {
                while (true) {
                    i++;
                    System.out.println(i);
                    //Thread.interrupted()  清除中断标志位
                    //Thread.currentThread().isInterrupted() 不会清除中断标志位
                    if (Thread.currentThread().isInterrupted() ) {
                        System.out.println("=========");
                    }
                    if(i==10){
                        break;
                    }

                }
            }
        });

        t1.start();
        //不会停止线程t1,只会设置一个中断标志位 flag=true
        t1.interrupt();
    }
利用中断机制优雅的停止线程
while (!Thread.currentThread().isInterrupted() && more work to do) {     do more work public class StopThread implements Runnable { @Override public void run() { int count = 0; while (!Thread.currentThread().isInterrupted() && count < 1000) { System.out.println("count = " + count++); } System.out.println("线程停止: stop thread"); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopThread()); thread.start(); Thread.sleep(5); thread.interrupt(); }
注意:使用中断机制时一定要注意是否存在中断标志位被清除的情况
sleep 期间能否感受到中断
修改上面的代码,线程执行任务期间有休眠需求
@Override
public void run() {
    int count = 0;
    while (!Thread.currentThread().isInterrupted() && count < 1000) {
        System.out.println("count = " + count++);

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("线程停止: stop thread");

处于休眠中的线程被中断,线程是可以感受到中断信号的,并且会抛出一个 InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false。这样就会导致while条件Thread.currentThread().
isInterrupted()为false,程序会在不满足count < 1000这个条件时退出。如果不在catch中重新手动添加中断信号,不做任何处理,就会屏蔽中断请求,有可能导致线程无法正确停止。
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); //重新设置线程中断状态为true Thread.currentThread().interrupt(); sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位 wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位
Java线程间通信
volatile
volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。
public class VolatileDemo {

    private static volatile boolean flag = true;

    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    if (flag){
                        System.out.println("trun on");
                        flag = false;
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    if (!flag){
                        System.out.println("trun off");
                        flag = true;
                    }
                }
            }
        }).start();
    }
等待唤醒(等待通知)机制
等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被唤醒。
public class WaitDemo {
    private static Object lock = new Object();
    private static  boolean flag = true;

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    while (flag){
                        try {
                            System.out.println("wait start .......");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    System.out.println("wait end ....... ");
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                if (flag){
                    synchronized (lock){
                        if (flag){
                            lock.notify();
                            System.out.println("notify .......");
                            flag = false;
                        }

                    }
                }
            }
        }).start();
    }
LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。使用它可以在任何场合使线程阻塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒
操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样的。
public class LockSupportTest {

    public static void main(String[] args) {
        Thread parkThread = new Thread(new ParkThread());
        parkThread.start();

        System.out.println("唤醒parkThread");
        LockSupport.unpark(parkThread);
    }

    static class ParkThread implements Runnable{

        @Override
        public void run() {
            System.out.println("ParkThread开始执行");
            LockSupport.park();
            System.out.println("ParkThread执行完成");
        }
    }
管道输入输出流
管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、
PipedInputStream、PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。
public class Piped {
    public static void main(String[] args) throws Exception {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        // 将输出流和输入流进行连接,否则在使用时会抛出IOException
        out.connect(in);

        Thread printThread = new Thread(new Print(in), "PrintThread");

        printThread.start();
        int receive = 0;
        try {
            while ((receive = System.in.read()) != -1) {
                out.write(receive);
            }
        } finally {
            out.close();
        }
    }

    static class Print implements Runnable {
        private PipedReader in;

        public Print(PipedReader in) {
            this.in = in;
        }

        @Override
        public void run() {
            int receive = 0;
            try {
                while ((receive = in.read()) != -1) {
                    System.out.print((char) receive);
                }
            } catch (IOException ex) {
            }
        }
    }
Thread.join
join可以理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但是如果调用线程的join方法其
实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串行的,最后join的实现其实是基于等待通知机制的

 

使用wait/notify阻塞唤醒线程

public class WaitTest {

 public void testWaitMethod(Object lock) {
 try {
 synchronized (lock) {
 System.out.println(Thread.currentThread().getName()+" begin wait()");
 // wait() 释放锁资源
 Thread.sleep(5000);

 lock.wait();
 System.out.println(Thread.currentThread().getName()+" end wait()");
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

 public void testNotifyMethod(Object lock) {
synchronized (lock) {
 System.out.println(Thread.currentThread().getName()+" begin notify()");

 lock.notify();

 System.out.println(Thread.currentThread().getName()+" end notify()");
 }
 }

 public static void main(String[] args) {
 Object lock = new Object();
 WaitTest test = new WaitTest();

 Thread t1 = new Thread(new Runnable() {
 @Override
 public void run() {
 test.testWaitMethod(lock);
 }
 },"threadA");
 t1.start();

 Thread t2 = new Thread(new Runnable() {
 @Override
 public void run() {
 test.testNotifyMethod(lock);
 }
 },"threadB");

 t2.start();
 try {
 Thread.sleep(500000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }

 }
 }

使用wait,notify来实现等待唤醒功能至少有两个缺点:
wait和notify都是Object中的方法,在调用这两个方法前必须先获
得锁对象,这限制了其使用场合:只能在同步代码块中。
当对象的等待队列中有多个线程时,notify只能随机选择一个线
程唤醒,无法唤醒指定的线程
使用LockSupport的话,我们可以在任何场合使线程阻塞,同时也可以指
定要唤醒的线程,相当的方便。
使用park/unpark阻塞唤醒线程
LockSupport是JDK中用来实现线程阻塞和唤醒的工具。使用它可以在任何场合使线程阻
塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次
唤醒的效果和一次唤醒是一样的。JDK并发包下的锁和其他同步工具的底层实现中大量使用
了LockSupport进行线程的阻塞和唤醒,掌握它的用法和原理可以让我们更好的理解锁和其
它同步工具的底层实现
 public class LockSupportTest {

 public static void main(String[] args) {
 Thread parkThread = new Thread(new ParkThread());
 parkThread.start();

 System.out.println("唤醒parkThread");
 LockSupport.unpark(parkThread);
 }

 static class ParkThread implements Runnable{

 @Override
 public void run() {
 System.out.println("ParkThread开始执行");
 LockSupport.park();
 System.out.println("ParkThread执行完成");
 }
 }
 }

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2022-07-24 20:32  VNone  阅读(63)  评论(0)    收藏  举报