Java核心技术之并发

Java并发

何时需要多线程

  • 程序需要同时执行两个或多个任务。
  • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  • 需要一些后台运行的程序时。

一、线程的使用

继承Thread类

需要在类中实现run()方法,因为Thread类也实现了Runnable接口。

当调用start()方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的run()方法。

class MyThread extends Thread{
    public void run(){
        //...
    }
}
public static void main(String[] args) {
    MyThread mt = new MyThread();
    mt.start();
}

实现Runnable接口

需要实现接口中的run()方法

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // ...
    }
}

使用Runnable实例再创建一个Thread实例,然后调用Thread实例的start()方法来启动线程

public static void main(String[] args) {
    MyRunnable instance = new MyRunnable();
    Thread thread = new Thread(instance);
    thread.start();
}

实现Callable接口

与Runnable相比

  • call()可以有返回值,返回值通过FutureTest进行封装
  • call()可以抛出异常,被外面的操作捕获,获取异常的信息
  • Callable是支持泛型的
class CallableThread implements Callable{
    public Object call() throws Exception {
        //...
        return 123;
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
    CallableThread ct = new CallableThread();
    FutureTask<Object> ft = new FutureTask<>(ct);
    Thread t = new Thread(ft);
    t.start();
    System.out.println(ft.get());
}

实现接口VS继承Thread

实现接口相比于继承更加好

  • Java不支持多重继承,因此继承了Thread类就无法继承其它类,但是可以实现多个接口。
  • 类可能只要求可执行就行,继承整个Thread类开销过大。

线程池

优势

  1. 提高响应速度(减少了创建新线程)

  2. 降低资源消耗(重复利用了线程池中的线程,不需要每次都创建)

  3. 便于线程管理

    corePoolSize:核心池大小

    maximumPoolSize:最大线程数

    keepAliveTime:线程没有任务时最多保持多长时间后会终止

class NumberThread implements  Runnable{
    public void run() {
        //...
    }
}
public static void main(String[] args) {
    ExecutorService service = Executors.newFixedThreadPool(10);//提供线程数量
    service.execute(new NumberThread());//执行指定的线程操作
    service.shutdown();//关闭连接池
}

二、线程的同步机制

synchronized

  1. 同步代码块

    synchronized(同步监视器){    //需要被同步的代码}
    

    说明

    1. 操作共享数据的代码,不能包含代码多了,也不能包含代码少了。
    2. 共享数据:多个线程共同操作的数据。
    3. 同步监视器:俗称:锁。任何一个类的对象,都可以充当锁。(要求多个线程必须共用一把锁)
  2. 同步方法

    如果操作数据共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。

    非静态同步方法,同步监视器是:this

class Window3 implements Runnable{
    private int ticket = 100;

    public void run() {
        while(true){
            show();
            if(ticket <= 0){
                break;
            }
        }
    }
    
    private synchronized void show(){//同步监视器:this
        if(ticket > 0){
            System.out.println(Thread.currentThread().getName() + ":" + "票号为:" + ticket);
            ticket --;
        }
    }
}

静态的同步方法,同步监视器是:window.class

class Window extends Thread{
    private static int ticket = 100;

    public void run() {
        while(true){
            show();
        }
    }

    private static synchronized void show(){//同步监视器:Windows4.class
        //同步监视器:t1,t2,t3
        if(ticket > 0){
            System.out.println(Thread.currentThread().getName() + ":买票, 票号为:" + ticket);
            ticket --;
        }
    }
}

Lock

public class LockExample {

    private Lock lock = new ReentrantLock();

    public void func() {
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        } finally {
            lock.unlock(); // 确保释放锁,从而避免发生死锁。
        }
    }
}

synchronized与Lock

  1. 锁的实现

    synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。

  2. 性能

    新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。

  3. 等待可中断

    当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。

    ReentrantLock 可中断,而 synchronized 不行。

  4. 公平锁

    公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。

    synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。

使用选择

除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。

三、线程之间的通讯

wait()/notify()/notifyAll()

  • wait():当前线程就进入阻塞状态,并释放同步监视器。
  • notify():唤醒wait的一个线程。如果有多个线程wait,就唤醒优先级高的。
  • notifyAll():唤醒所有被wait的线程。

它们都属于 Object 的一部分,而不属于 Thread。

wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。否则会抛出IllegalMonitorStateException异常。

wait() 和 sleep() 的区别

  • wait()是Object的方法,而sleep()是Thread的静态方法。
  • wait()会释放锁,而sleep()不会。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值