
简介
并发编程的目的是为了让程序运行的更快,但是,并不是启动更多的线程就能让程序最大限度的并发执行。让程序变快,会有很多挑战,比如:死锁、上下文切换
1. 上下文切换
CPU是通过给每隔线程分配CPU时间片来实现这个机制。时间片就是CPU分配给每个线程的时间。因为时间非常短,一般就是几十毫秒。
CPU是通过时间片的分配来循环执行任务的,当前任务执行一个时间片会自动切换到下一个任务,但是,在切换前会保存上一个任务的状态,以便下次切回来这个任务时,可以加在这个任务的状态。这个任务从保存到切换在加载回来的过程就是上下文切换。
2. 多线程不一定快
当并发累加操作不超过百万时,速度会比串行执行的累加要慢,因为线程有创建和切换上下文的开销
3. 如何减少上下文
- 无锁并发编程:多线程竞争锁时,会引起上下文的切换。多用些不同线程处理不同不同数据段的方法
- CAS算法: java的atomic包使用CAS算法来更新数据的,不需要锁
- 避免创建不需要的线程,任务少,线程多,这样大部分线程处于等待状态
4. 死锁
public class Test3 { private static String A = "a"; private static String B = "b"; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (A) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1 sync A"); synchronized (B) { System.out.println("t1 sync B"); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (B) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2 sync B"); synchronized (A) { System.out.println("t2 sync A"); } } } }); t1.start(); t2.start(); }}
5. 避免死锁
- 避免一个线程使用多个锁
- 避免一个线程在所内同事占用多个资源,尽量保证在锁期间就占用一个资源
6. 资源限制
- 服务器的带宽只有2Mb/s,某个资源的下载速度是1Mb/s每秒,系统启动10线程,速度一定是到不了10MB/s
多线程状态和方法
1. 状态
线程共包括以下 5 种状态:
新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
2. 方法
- void join() 等待该线程终止 相当于插队
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。主线程本来要比thread2线程先执行完成,但加入join后,主线程会让thread2先执行完成,然后自己执行完成。
MyThread2 myThread2 = new MyThread2(); Thread t2 = new Thread(myThread2, "join"); t2.start(); try { t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("join end"); class MyThread2 implements Runnable { // 实现Runnable接口 @Override public void run() { // 覆写run()方法 for (int i = 0; i < 10; i++) { System.out.println(i); } }}
- static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行
- static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
- wait(), notify() 两个方法是暂停和唤醒。用于生产者和消费者模式
3. 三种创建多线程
- 1.Thread 类也是 Runnable 接口的子类
- 2.使用Runnable,代码可以被多个线程共享,代码和数据独立
- 3.线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
public static void main(String[] args) { new Thread(new MyThread1(),"Runnable").start(); new MyThread2().start(); new Thread(() -> { System.out.println("Lambda"); }).start();}static class MyThread1 implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); }}static class MyThread2 extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()); }}