JAVA中的多线程
几个基本的概念
-
程序:代码的集合,是静态的
-
进程:程序的一次执行(运行着的程序),是动态的
-
线程:进程中可以并行执行的小任务(例如QQ,迅雷)
操作系统中的 -
进程:当一个程序进入内存运行时就变成了一个进程(是系统进行资源分配和调度的一个独立单位),具有独立性,动态性。并发性(程序是静态的指令集合,而进程是动态的)
-线程:线程是进程的组成部分,是进程的执行单元,一个进程可有拥有多个线程,一个线程必须有父线程
java中多线程的作用: -
发挥多核cpu的优势
-
防止阻塞
-
便于建模
创建线程的方式 -
继承Thread类,重写run()的方法
用Thread创建多线程的常用步骤 -
定义一个类(A),来继承Thread, 同时重写run方法
-
创建线程对象 (A a=new A()????
-
启动线程(a.start())
-
实现Runnable接口
-
定义一个类,实现Runnable接口;重写run方法;
-
定义一个Thread(Runnable ),将Runnable作为参数传递给Thread
后者相对来说好一些:实现接口的方式比继承跟家灵活 和方便,同时也能降低程序之间的耦合度。
两个juc的方法 -
Callable接口(juc)
-
线程池(juc)
Volatile关键字的作用: -
多线程主要围绕可见性和原子性展开的,使用Volatile关键字修饰变量时保证了多线程之间的可见性,每次读取的Volatile变量都是最新的数据
-
Volatile禁止语义重排序,这在一定程度上降低了代码的执行效率
什么是线程安全: -
不可变:像String,Long,Integer这些,都是final类,任何一个线程都改变不了他们的值,要想改变只能重新创建一个。
-
绝对线程安全:CopyOnWriterArrayList
-
相对线程安全:vector
-
线程非安全:arraryList,LinkedList,HanshMap
当多个线程对一个资源(对象,变量)进行操作时,可能会引发线程安全的问题
资源竞争的问题: -
当多个线程访问一个资源时,可能会出现资源竞争的问题,我们可以通过同步锁来解决此问题,就是将访问的资源的代码放在同一个同步块或同步方法中,当需要执行此代码时,必须先得到锁才能执行,如果得不到锁不能执行,只能将线程放在锁池中等待其它线程释放锁。
使用同步块或同步方法可以解决线程并发遇到的安全问题 -
同步块:
synchronized(monitor){}
monitor可以时任何对象,要想运行同步块中的代码,必须先获得monitor,同一时刻只能使用同一个monitor的一个线程获得monitor对象,块中的代码执行完毕后,monitor会自动释放。
同步方法中使用的monitor是this -
如果某个synchronized方法是static的,那么当线程访问该方法时它锁的并不是synchronized方法所在的对象,而是synchronized方法所在对象所对应的class对象,因为java中无论一个类有多少个对象,这些对象会对唯一一个class对象,因此当线程分别访问同一个类的两个对象的两个static.
-
synchronized方法,他们执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
-
关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)
守护线程 -
该线程的优先级比较低,如果进程中只有守护线程还在运行,那么操作系统不会等待守护线程运行结束,gc就是一个守护线程。
一个线程在运行时出现了异常会怎么样:
-
如果这个异常没有捕获的话,这个线程就会停止运行,如果这个线程的某个对象有监视器,这时就会释放。
线程之间是如何共享数据的
是通过共享对象来实现的,通过wait,notify,ntifyall,await,signal,signalall 进行唤醒和等待。
sleep 和wait的区别 -
wait是object方法,sleep是thread的方法。
-
sleep阻塞一段时间后进入就绪态而wait就如等待队列,直到使用同一个锁对象的线程调用notify或notifyll方法才能将其唤醒。
-
wait只能在同步块或同步方法中使用,而sleep不需要
-
sleep不会释放锁,而wait会释放锁。
线程的生命周期
新生-》就绪-》运行-》阻塞-》消亡