线程问题
Posted interfaceaop
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程问题相关的知识,希望对你有一定的参考价值。
线程的入门
在了解线程之前,首先明白什么是进程。
什么是进程:
进程是指运行中的应用程序,每个进程都会有自己独立的地址空间(内存空间)比如:浏览器,编译器,系统任务管理器等等。操作系统会给该进程分配独立的地址空间,当用户再次点击浏览器时,就又启动一个进程。用户每启动一个进程,操作系统就会给该进程分配一个独立的内存空间。
什么是线程:
线程可以理解成进程的寄生,一个进程可以由多个线程。线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但他可与同属一个进程的其他线程共享所拥有的全部资源,一个线程可以创建和撤销另一个线程,线程可以并发执行。
线程有三种基本状态:就绪,阻塞,运行。
线程的理解:
1、线程是轻量级进程。
2、线程没有独立的地址空间
3、线程由进程创建(寄生)
4、一个进程可以拥有多个线程------>多线程
5、线程的生命周期:
新建状态,就绪状态,运行状态,阻塞状态,死亡状态。
线程的应用场景:
1、我们熟悉的Tomcat服务器内部就是使用多线程:
上百个客户访问同一个web应用,Tomcat接入后都是把后续的处理扔给一个新的线程来处理,这个新的线程就是servlet程序,比如doGet和doPost
如果不采用多线程,上百个用户同时访问一个web应用的时候,只能使用串行处理,那样就不实际。
2、异步处理:
异步处理也使用多线程,比如task a和task b要并行处理。但是CPU是多核的话,就可以让一个CPU执行有一个线程,如果只有一个CPU的话,底层是按照分时复用的原则,各个线程按照时间获取CPU资源。
3、javaweb应用中很少用到多线程,因为在开发过程中,多线程被框架实现了,需要自己实现的很少。
4、用的比较多的时网络应用程序。
线程的安全问题:
为什么又线程安全问题?
当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
经典案例就是抢火车票的案例啦:
class ThreadTrain1 implements Runnable { private int count = 100; private static Object oj = new Object();
@Override public void run() { while (count > 0) { try { Thread.sleep(50); } catch (Exception e) { // TODO: handle exception } sale(); } }
public void sale() { // 前提 多线程进行使用、多个线程只能拿到一把锁。 // 保证只能让一个线程 在执行 缺点效率降低 // synchronized (oj) { // if (count > 0) { System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票"); count--; // } // } } }
public class ThreadDemo { public static void main(String[] args) { ThreadTrain1 threadTrain1 = new ThreadTrain1(); Thread t1 = new Thread(threadTrain1, "①号窗口"); Thread t2 = new Thread(threadTrain1, "②号窗口"); t1.start(); t2.start(); } } |
运行结果:
可以看到一号跟二号出现抢到同一张票,这显然是不可用的
线程安全的解决办法:
使用同步synchronized或者使用锁(lock)
为什么使用多线程同步或者锁就能解决线程安全问题?
答:因为多线程可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程执行,同步/锁内的代码执行完之后,才能让其他线程进来执行,一个一个执行,这样就可以解决线程不安全问题。多个线程共享一个资源,不会收到其他线程的干扰。
多线程同步代码块案例:
private static Object oj = new Object(); public void sale() { // 前提 多线程进行使用、多个线程只能拿到一把锁。 // 保证只能让一个线程 在执行 缺点效率降低 synchronized (oj) { if (count > 0) { System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票"); count--; } } } |
也可以这样写:
public synchronized void sale() { if (trainCount > 0) { try { Thread.sleep(40); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票."); trainCount--; } } |
注意:使用同步代码块的时候一般都是使用当前类。静态方法时使用锁时当前类的字节码文件。
多线程死锁:
简单理解:同步中嵌套同步,导致锁无法释放。
class ThreadTrain6 implements Runnable { // 这是货票总票数,多个线程会同时共享资源 private int trainCount = 100; public boolean flag = true; private Object mutex = new Object();
@Override public void run() { if (flag) { while (true) { synchronized (mutex) { // 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁. // 如果flag为true 先拿到 obj锁,在拿到this 锁、 才能执行。 // 如果flag为false先拿到this,在拿到obj锁,才能执行。 // 死锁解决办法:不要在同步中嵌套同步。 sale(); } } } else { while (true) { sale(); } } } public synchronized void sale() { synchronized (mutex) { if (trainCount > 0) { try { Thread.sleep(40); } catch (Exception e) {
} System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票."); trainCount--; } } } }
public class DeadlockThread {
public static void main(String[] args) throws InterruptedException {
ThreadTrain6 threadTrain = new ThreadTrain6(); // 定义 一个实例 Thread thread1 = new Thread(threadTrain, "一号窗口"); Thread thread2 = new Thread(threadTrain, "二号窗口"); thread1.start(); Thread.sleep(40); threadTrain.flag = false; thread2.start(); }
} |
多线程的三大特性:
1、原子性
原子性是指不可分割,必须前后一致,比如去银行存取钱,A账户往B账户里转1000块钱,A账户里减去1000,B账户增加1000,前后不可以有分割。
2、可见性
多个线程访问同一个变量时,一个线程修改了该变量的值,其他线程能够立即看到修改的值
3、有序性
程序执行的顺序按照代码的先后顺序执行。
Java内存模型:
共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
以上是关于线程问题的主要内容,如果未能解决你的问题,请参考以下文章