• Java并发


    Java并发

    Java线程

    线程的状态

    新建(New)

    创建后尚未启动。

    可运行(Runnable)

    可能正在运行,也可能正在等待CPU时间片

    包含了操作系统线程状态中的Running和Ready

    阻塞(Blocked)

    等待获取一个排它锁,如果其他线程释放了锁就会结束此状态

    无限期等待(Waiting)

    等待其他线程显示地唤醒,否则不会分配CPU的时间片。

    限期等待(Time Waiting)

    无需等待其他线程显示地唤醒,在一定时间之后会被系统自动唤醒。

    阻塞和等待有什么区别?

    阻塞和等待的区别在于,阻塞是被动的,它是在等待获取一个排它锁。而等待是主动的,通过调用Thread.sleep()和Object.wait()登方法进入。

    线程的创建方式

    有三种使用线程的方法

    • 实现Runnable接口。
    • 实现Callable接口
    • 继承Thread类

    实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。

    实现Runnable接口

    需要实现run()方法。

    通过Thread调用start()方法来启动线程。

    public class MyRunnable implements Runnable {

        public void run() {

        // ...

        }

    }

    public static void main(String[] args) {

        MyRunnable instance = new MyRunnable();

        Thread thread = new Thread(instance);

        thread.start();

    }

    实现Callable接口

    与Runnable相比,Callable可以由返回值,返回值通过FutureTask进行封装

    public class MyCallable implements Callable<Integer> {

        public Integer call() {

            return 123;

        }

    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        MyCallable mc = new MyCallable();

        FutureTask<Integer> ft = new FutureTask<>(mc);

        Thread thread = new Thread(ft);

        thread.start();

        System.out.println(ft.get());

    }

    继承Thread类

    同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。

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

    public class MyThread extends Thread {

        public void run() {

        // ...

        }

    }

    public static void main(String[] args) {

        MyThread mt = new MyThread();

        mt.start();

    }

    实现接口VS继承Thread

    实现接口会更好一些,因为:

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

    互斥同步

    Synchronized与ReentrantLock

    Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是JDK 实现的 ReentrantLock。

    Synchronized

    同步一个代码块

    public void func() {

        synchronized (this) {

        // ...

        }

    }

    它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。

    对于以下代码,使用 ExecutorService 执行了两个线程,由于调用的是同一个对象的同步代码块,因此这两个线程会进行同步,当一个线程进入同步语句块时,另一个线程就必须等待。

    public class SynchronizedExample {

        public void func1() {

            synchronized (this) {

                for (int i = 0; i < 10; i++) {

                    System.out.print(i + " ");

                }

            }

        }

    }

    public static void main(String[] args) {

        SynchronizedExample e1 = new SynchronizedExample();

        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.execute(() -> e1.func1());

        executorService.execute(() -> e1.func1());

    }

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

    对于以下代码,两个线程调用了不同对象的同步代码块,因此这两个线程就不需要同步。从输出结果可以看出,两个线程交叉执行。

    public static void main(String[] args) {

        SynchronizedExample e1 = new SynchronizedExample();

        SynchronizedExample e2 = new SynchronizedExample();

        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.execute(() -> e1.func1());

        executorService.execute(() -> e2.func1());

    }

    0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

    同步一个方法

    public synchronized void func () {

        // ...

    }

    同步一个类

    public void func() {

        synchronized (SynchronizedExample.class) {

            // ...

        }

    }

    作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步

    public class SynchronizedExample {

        public void func2() {

            synchronized (SynchronizedExample.class) {

                for (int i = 0; i < 10; i++) {

                    System.out.print(i + " ");

                }

            }

        }

    }

    public static void main(String[] args) {

        SynchronizedExample e1 = new SynchronizedExample();

        SynchronizedExample e2 = new SynchronizedExample();

        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.execute(() -> e1.func2());

        executorService.execute(() -> e2.func2());

    }

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

    同步一个静态方法

    public synchronized static void fun() {

        // ...

    }

    作用于整个类

    ReentrantLock

    ReentrantLock java.util.concurrentJ.U.C)包中的锁。

    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(); // 确保释放锁,从而避免发生死锁。

            }

        }

    }

    public static void main(String[] args) {

        LockExample lockExample = new LockExample();

        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.execute(() -> lockExample.func());

        executorService.execute(() -> lockExample.func());

    }

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

    比较

    synchronizedJVM实现的,而ReentrantLockJDK实现的。

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

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

    Java集合类有哪些是线程安全的?

    vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。

    statck:堆栈类,先进后出

    hashtable:就比hashmap多了个线程安全

    enumeration:枚举,相当于迭代器

    除了这些之外,其他的都是非线程安全的类和接口。

    线程安全的类其方法是同步的,每次只能一个访问。是重量级对象,效率较低。

    线程安全是指任何时刻都只有一个线程访问临界资源。线程安全 并不是说他的一系列操作是同步的 只是对于他执行某个方法的时候不允许别的线程去改变。针对一个类来说是不是线程安全就要看,多个线程在同时在运行,这些线程可能会同时执行某个方法。但是每次运行结果和单线程执行的结果一样,那么就可以说是线程安全的。

    java.util.concurrent 包添加了多个新的线程安全集合类(ConcurrentHashMap、CopyOnWriteArrayList 和CopyOnWriteArraySet)这些类的目的是提供高性能、高度可伸缩性、线程安全的基本集合类型版本

    通过同步的封装工厂(Collections.synchronizedMap()、synchronizedList() 和 synchronizedSet()),非线程安全集合均可表现为线程安全的

  • 相关阅读:
    结构struct 联合Union和枚举Enum的细节讨论
    ubuntu 查询 修改 时间
    在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)
    浅谈Android系统开发中LOG的使用
    如何单独编译Android源代码中的模块
    在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
    在Ubuntu上为Android系统编写Linux内核驱动程序
    在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务
    在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
    在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
  • 原文地址:https://www.cnblogs.com/kexinxin/p/11608050.html
Copyright © 2020-2023  润新知