-
Notifications
You must be signed in to change notification settings - Fork 0
kuangye89757/ThreadDemo
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
尊重原著 一切来自http://www.cnblogs.com/skywang12345/p/3479024.html指导整理 ================================================================================================== 说明: 线程共包括以下5种状态。 1. 新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。 2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。 3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。 4. 阻塞状态(Blocked) : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: (01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。 (02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。 (03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 5. 死亡状态(Dead) : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。 这5种状态涉及到的内容包括 Object类---定义了wait(), notify(), notifyAll()等休眠/唤醒函数。 Thread类---定义了一系列的线程操作函数。sleep()休眠函数, interrupt()中断函数, getName()获取线程名称等。 synchronized---关键字;区分为synchronized代码块和synchronized方法。synchronized的作用是让线程获取对象的同步锁。 ================================================================================================== synchronzied 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限, 在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized修饰的代码); 线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。 这样就保证了同步代码在统一时刻只有一个线程在执行。 ================================================================================================== 线程基础知识和栗子 -- [见com.kuangye.threaddemo.basis对应包下的实例] ================================================================================================== start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。 start()不能被重复调用。 run() : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程! ================================================================================================== 我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。 第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。 第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。 第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。 ================================================================================================== 在Object.java中,定义了wait(), notify()和notifyAll()等接口。 wait()的作用是让当前线程【进入阻塞状态,同时让释放当前线程它所持有的锁】 而notify()和notifyAll()的作用,则是唤醒当前对象上的阻塞线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。 Object类中关于等待/唤醒的API详细信息如下: notify() -- 唤醒在此对象监视器上等待的单个线程。 notifyAll() -- 唤醒在此对象监视器上等待的所有线程。 wait() -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。 wait(long time) -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。 wait(long timeout, int nanos) -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。 ================================================================================================== 为什么notify(), wait()等函数定义在Object中,而不是Thread中? Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。 wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它所持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行! OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。 现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的? 答案是:依据“对象的同步锁”。 负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个), 并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。 【被唤醒不能立刻执行,必须等到唤醒线程释放了“对象的同步锁”之后(一般是唤醒线程的同步代码块执行结束之后),等待线程才能获取到“对象的同步锁”进而继续运行】 ================================================================================================== yield()的作用是让步/谦让 它能让当前线程由“运行状态”进入到“就绪状态”,重新竞争 CPU 的调度权 可能会获取到,也有可能被其他线程获取到 可以很好的控制多线程,如执行某项复杂的任务时,如果担心占用资源过多, 可以在完成某个重要的工作后使用 yield 方法让掉当前 CPU 的调度权,等下次获取到再继续执行, 这样不但能完成自己的重要工作,也能给其他线程一些运行的机会,[避免一个线程长时间占有CPU资源] yield()方法不会释放锁 所以已经获取锁的线程 让出来CPU只有可能让不需要该锁的线程可能获取到CPU ================================================================================================== yield() 与 wait()的比较? wait()是让线程由“运行状态”进入到“阻塞状态”, yield()是让线程由“运行状态”进入到“就绪状态”。 wait()是会线程释放它所持有对象的同步锁, yield()方法不会释放锁。 ================================================================================================== sleep() 与 wait()的比较? wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。 会将当前线程放入wait set,等待被唤醒,并放弃lock对象上的所有同步声明 只有通过以下四个方法可以主动唤醒: 1. notify 2. notifyAll 3. Thread.interrupt() 4. 等待时间过完。 当线程被唤醒后,线程就从wait set中移除了并且重新获得线程调度能力 sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”的同时,并不会释放锁 ================================================================================================== join说明 t1.join(); 则当前线程需要等待t1线程执行结束才能继续执行 (当前线程进行中...t1加入,当前线程等待....t1进行中...t1结束...当前线程继续) ================================================================================================== interrupt()说明 见 interrupt()的作用是中断本线程。 线程中断自己 是被允许的;其它线程调用该线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。 使得线程是处于阻塞状态: 1.调用线程的wait()系列方法进入阻塞状态 2.调用线程的join()系列方法, sleep()系列方法进入阻塞状态 当且仅当线程在【阻塞状态】时,调用它的interrupt()方法,那么它的“中断标记”会被清除并收到一个InterruptedException异常。 例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;之后会立即将该线程的中断标记设为“true”, 但由于线程处于阻塞状态,该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。 interrupted() 和 isInterrupted()的区别 interrupted() 除了返回中断标记之外,它还会清除中断标记(将中断标记设为false);(当且仅当线程在【阻塞状态】时) 而isInterrupted()仅仅返回中断标记。 通过中断机制,将阻塞状态的线程打断;这也就是为什么wait和sleep等方法 需要try...catch InterruptedException的异常 ================================================================================================== 过时方法 (使用时容易造成线程死锁) suspend : 使线程暂停,但不会释放锁资源;需要等待resume;而准备resume的线程被其他锁锁定时,就会造成死锁 (不同于wait和notify机制是因为 它持有锁的情况下进入阻塞状态,而又不像sleep那样有超时机制) resume : 使线程恢复,若之前没有suspend则无效 (同上) stop : 停止当前线程 不会保证释放资源 (同上) 代替方案: 1.中断机制 2.唤醒机制 3.volatile标识判断机制 如何正确的停止一个线程 见StopThreadTest.java 通过简单修改 volatile变量的值 来指示其目标线程将停止运行的代码 ================================================================================================== 线程优先级介绍 线程优先级的范围是1~10,默认的优先级是5。“高优先级线程”会优先于“低优先级线程”执行 java 中有两种线程:用户线程和守护线程。 可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。 每个线程都可以被标记为一个守护进程或非守护进程。 在一些运行的主线程中创建新的子线程时,子线程的优先级被设置为等于“创建它的主线程的优先级”, 当且仅当“创建它的主线程是守护线程”时“子线程才会是守护线程”。 当Java虚拟机启动时,通常有一个单一的非守护线程(该线程通过是通过main()方法启动)。 JVM会一直运行直到下面的任意一个条件发生,JVM就会终止运行: (01) 调用了exit()方法,并且exit()有权限被正常执行。 (02) 所有的“非守护线程”都死了(即JVM中仅仅只有“守护线程”)。即当只有守护线程运行时,JVM会自动退出。 ================================================================================================== 经典生产/消费者模型 [com.kuangye.threaddemo.basis.classic] ================================================================================================== “JUC原子类”---所谓原子操作,是指操作过程不会被中断,保证数据操作是以原子方式进行的。 ================================================================================================== 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ; 2. 数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray ; 3. 引用类型: AtomicReference, AtomicStampedRerence, AtomicMarkableReference ; 4. 对象的属性修改类型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 。 这些类存在的目的是对相应的数据进行原子操作。
About
线程相关知识点总结
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published