Java多线程

2021/11/13 17:10:58

本文主要是介绍Java多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线程创建方式

  • 继承Thread类:实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程,线程类已经继承Thread类了,就不能再继承其他类,多个线程不能共享同一份资源(如前面分析的成员变量 i )
  • 实现Runnable接口:线程类只是实现了接口,还可以继承其他类,多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况。要访问当前线程,必须调用Thread.currentThread()方法。
  • 通过Callable和Future接口创建线程:上述两种方法都不能有返回值,且不能声明抛出异常。而Callable接口则实现了此两点,Callable接口如同Runable接口的升级版,其提供的call()方法将作为线程的执行体,同时允许有返回值。

Future

  • get:方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
  • get(long timeout,TimeUnit unit):做多等待timeout的时间就会返回结果
  • cancel(boolean mayInterruptIfRunning):方法可以用来停止一个任务,如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false.
  • isDone():方法判断当前方法是否完成
  • isCancel():方法判断当前方法是否取消

Callable Future示例

public class FutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}

class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("call");
        return "MyCallable";
    }
}

改变线程状态

  • Thread.sleep(long millis) 线程调用此方法,则进入RIMED.WAITING状态,但不释放锁,millis后线程自动苏醒进入就绪状态。作用:给其他线程执行的最佳机会。
  • Thread.yield():线程调用此方法,当前线程放弃获取的CPU时间片,但不释放资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不一定会轮流执行,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中,Thread.yield()不会阻塞线程。该方法sleep()类似,只是不能由用户指定暂停时长。
  • join()/join(long millis):当前线程调用其他线程的join()方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁,线程执行完或millis时间到,当前线程进入就绪状态。
  • wait:当前线程调用对象wait(),当前线程释放对象锁,进入等待队列。依靠notfiy()/notifyAll()唤醒或wait(long millis)时间到自然醒。
  • notfiy:唤醒此对象监视器上等待的单个线程,选择是任意。
  • notfiyAll:唤醒此对象监视器上等待的所有线程。

join 示例

public class Join {

    public static void main(String[] args) {
        Object object = new Object();
        MyThread myThread = new MyThread("myThread", object);
        myThread.start();

        synchronized (myThread) {
            for (int i = 0; i < 50; i++) {
                if (i == 20) {
                    try {
                        myThread.join();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
        }
    }
}

class MyThread extends Thread {
    private String name;
    private Object lock;


    public MyThread(String name, Object object) {
        this.name = name;
        this.lock = object;
    }

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 20; i++) {
                System.out.println(name + "-" + i);
            }
        }
    }
}

wait notifyAll 示例

public class WaitAndNotify {

    public static void main(String[] args) {
        MethodClass methodClass = new MethodClass();

        Thread thread1 = new Thread(() -> {
            try {
                methodClass.product();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "thread1");

        Thread thread2 = new Thread(() -> {
            try {
                methodClass.customer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "thread2");

        Thread thread3 = new Thread(() -> {
            try {
                methodClass.customer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "thread3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}


class MethodClass {
    final int MAX_COUNT = 20;

    int PRODUCT_COUNT = 0;

    public synchronized void product() throws Exception {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "::run::" + PRODUCT_COUNT);
            Thread.sleep(100);
            if (PRODUCT_COUNT >= MAX_COUNT) {
                System.out.println("暂停生产");
                wait();
            } else {
                PRODUCT_COUNT++;
            }
            notifyAll();
        }
    }

    public synchronized void customer() throws Exception {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "::run::" + PRODUCT_COUNT);
            Thread.sleep(100);
            if (PRODUCT_COUNT <= 0) {
                System.out.println("暂停消费");
                wait();
            } else {
                PRODUCT_COUNT--;
            }
            notifyAll();
        }
    }
}

volatile 关键字

  1. 在多线程环境下使用volatile变量能够保证每次读取前必须先从主内存刷新最新的值,每次写入后必须立即同步回主内存当中。也就是说,volatile关键字修饰的变量看到的随时是自己的最新值。但是volatile关键词不能保证原子性。
  2. 每次读、取强制从主内存刷数据,能防止字节码指令重排。
  3. 适用单线程写,多线程读。
  4. 原则:能不用就不用,不确定的时候也不用。
  5. 替代方案:Atomic原子操作类。

synchronized 关键字

  • 锁对象:锁当前对象
  • 锁方法:锁调用方的对象
  • 锁代码块:锁调用方的对象(推荐,最小粒度)


这篇关于Java多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程