多线程

来源:互联网 发布:c语言变量命名规范 编辑:程序博客网 时间:2024/04/28 16:13


进程
进程是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程
就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程至少有一个线程。

jvm启动时,会有一个进程java.exe。该进程中至少有一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
其实更细节的说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

创建线程的两种方式
继承Thread

//继承Thread创建线程class MyThread extends Thread{public void run(){}}//开启线程new MyThread().start();



实现Runnable接口

//实现Runnable接口class ImplRunnable extends Runnable{public void run(){}}//开启线程new Thread(new ImplRunnable).start();



多线程的安全问题
当多个线程在访问同一个共享数据时,会产生安全隐患,一个线程对数据访问还没有结束,另一个线程就开始访问共享数据,导致共享数据错误。

解决办法
对多个线程访问同一个共享数据,只能让一个线程访问完毕后,其他线程在访问,在线程访问时其他线程不允许访问。
java对于多线程的安全问题提供了专业的解决方式,同步代码块。

同步代码块

synchronized(对象){//需要被同步的代码;}


对象如同同步锁,持有锁的线程可以在同步中执行。
典型例子:毕向东老师说的,火车上的卫生间。

同步的前提
1.必须要有两个或者两个以上线程。
2.必须是多个线程使用同一个锁(类的class文件对象在内存中唯一的,唯一锁)
   必须保证同步中能有一个线程在运行

 

同步的好处和弊端
同步的好处:解决了多线程的安全问题
同步的弊端:多个线程需要判断锁,较为消耗资源。

如何查找安全问题
1.明确哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的

同步的两种形式
1.同步代码块(对象锁)
2.同步函数(this对象锁)
静态函数的函数锁是class,因为静态函数不可以定义this,它使用的是该方法所在类的字节码对象,静态进内存时,内存中没有本类对象,但是一定有该类对于的字节码文件对象类名.class它的类型是Class。

死锁
同步嵌套同步,而锁却不同,例如同步A需要A锁,同步B需要B锁,一个线程持有A锁,另一个线程持有B锁,A同步块要进入B同步块,同时B同步要进入A同步块,可能都没有释放所,就会出现死锁,程序停止那里不动了。

/*实现Runnable的Test类*/class Test implements Runnable {private boolean flag;Test(boolean flag){this.flag = flag;}public void run(){if(flag)//如果true执行if的同步代码块{synchronized(MyLock.locka){System.out.println("if...locka");synchronized(MyLock.lockb){System.out.println("if...lockb");}}}else//如果false执行else中的同步代码块{synchronized(MyLock.locka){System.out.println("else...locka");synchronized(MyLock.lockb){System.out.println("else...lockb");}}}}}/*要用到对象锁locka和lockb*/class MyLock{static Object locka = new Object();static Object lockb = new Object();}/*死锁测试*/class DeadLockTest{public static void main(String[] args){//开启两个线程让他们争夺cpu执行权,查看可能出现的死锁情况Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}



多线程等待唤醒机制图解 



 
 同步方法
wait:让线程处于等待状态,直到被notify或notifyAll唤醒,会释放对象锁。

notify:唤醒等待中的线程,通常是线程池中第一个等待线程。用于两个线程交互

notifyAll:唤醒线程池中所有等待线程。用于多个线程交互。

线程池

等待状态的线程都在内存中的线程池中。

注意:

wait、notify、notifyAll都是用在同步中,因为对持有监视器(锁)的线程操作。

为什么线程操作方法要定义在Object类中?

因为这些方法在操作同步线程时,都必须要标识他们操作线程的持有锁。只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,也就是说等待和唤醒必须是同一个锁,而锁可以是任意对象,所以定义在Object类中。

 

如何停止线程?

stop方法已过时,不再被使用。如何结束线程只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构只要控制住循环就可以让run方法结束,也就是线程结束。

 

特殊情况

当线程处于冻结状态,就不会读取到循环标记,那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结进行清除(interrupt方法 ),强制线程恢复到运行状态中来,这样就可以在cathc中操作标记让线程结束。

 

守护线程

public final void setDeamon(boolean on)将该线程标记为守护线程或用户线程。

当正在运行的线程都是守护线程时JVM退出。该方法必须在启动线程前调用

 

什么时候使用守护线程?

当该线程依赖于其他线程时,其他线程结束后它就没有存在的意义。标记为守护线程。

 

Join方法

当A线程执行到B线程的join方法时,A就会等待B线程执行完,A才会执行,join可以用来临时加入线程执行(当满足条件时)。

 

yield方法

暂停当前正在执行的线程对象,并执行其他线程。使线程交替执行,减少某个线程频繁执行的频率。


  • 大小: 2.4 KB
  • 查看图片附件
0 0