首页 > 编程语言 > 详细

Java并发

时间:2019-09-29 16:46:04      阅读:65      评论:0      收藏:0      [点我收藏+]

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

Java并发

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

(1)
(1)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!