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()),非线程安全集合均可表现为线程安全的

原文地址:https://www.cnblogs.com/kexinxin/p/11608050.html