Java并发
一、使用线程
//实现Runnable接口
public class MyRunnable implements Runnable { @Override public void run() { // ... } }
public static void main(String[] args) {
MyRunnable instance = new MyRunnable(); //Runnable实例
Thread thread = new Thread(instance); //Thread实例
thread.start(); //使用Thread实例的start()方法来启动线程
}
//实现Callable接口
public class MyCallable implements Callable<Integer> { public Integer call() { return 123; //与Runnable相比,Callable可以有返回值 } } public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable mc = new MyCallable(); //Callable实例 FutureTask<Integer> ft = new FutureTask<>(mc); //返回结果用FutureTask封装 Thread thread = new Thread(ft); //Thread实例 thread.start();//使用Thread实例的start()方法来启动线程 System.out.println(ft.get()); //输出返回值 }
//继承Thread类 /*当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,
当一个线程被调度时会执行该线程的 run() 方法。*/ public class MyThread extends Thread { public void run() { // ... } } public static void main(String[] args) { MyThread mt = new MyThread(); mt.start(); }
总之来说实现接口更好,因为:java不支持多重继承,因此继承Thread类之后便无法继承其他的类,但是可以实现多个接口;其次,继承整个Thread类开销过大。
二、基础线程机制
三、中断
四、互斥同步
Java主要提供了两种锁机制来控制多个线程对共享资源的互斥访问。
JVM实现的Synchronized和JDK实现的ReentrantLock
对于Synchronized:
同步一个代码块时:对同一个对象才会同步,如果作用于不同对象上的同步代码块,则不会同步。
同步一个方法时和同步代码块一样,也是作用于同一个对象才会同步
同步一个类时:两个线程调用同一个类的不同对象也会同步。
对于ReentrantLock:
同步从new ReentrantLock()开始,直到lock.unlock()的中间代码部分。
两者比较:
锁的实现
synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
性能
新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
等待可中断
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
ReentrantLock 可中断,而 synchronized 不行。
公平锁
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
锁绑定多个条件
一个 ReentrantLock 可以同时绑定多个 Condition 对象。
使用选择
除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。
五、线程之间的协作
1.join()