java 多线程 总结一
首先讲一下进程和线程的区别:
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。
一、拓展java.lang.Thread类
package com.thread.demo;
public class ThreadImplSecond extends Thread {
public ThreadImplSecond(String name){
super(name);
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
System.out.println("current thread name is:"+getName()+",vuale is :"+i);
}
}
}
package com.demo.test;
import com.thread.demo.ThreadImplSecond;
public class ThreadImplSecondTest {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.out.println("main thread is start");
ThreadImplSecond impl =new ThreadImplSecond("child thread");
impl.start();
System.out.println("main thread name is :"+Thread.currentThread().getName());
System.out.println("main thread is end");
}
}
输出结果: main thread is start
main thread is end
current thread name is:child thread,vuale is :0
current thread name is:child thread,vuale is :1
current thread name is:child thread,vuale is :2
current thread name is:child thread,vuale is :3
current thread name is:child thread,vuale is :4
current thread name is:child thread,vuale is :5
current thread name is:child thread,vuale is :6
current thread name is:child thread,vuale is :7
current thread name is:child thread,vuale is :8
current thread name is:child thread,vuale is :9
package com.thread.demo;
public class ThreadImpl extends Thread {
public ThreadImpl(String name){
super(name);
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
System.out.println("current thread name is:"+getName()+",vuale is :"+i);
}
}
}
package com.demo.test;
import com.thread.demo.ThreadImpl;
public class ThreadImplTest {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.out.println("main thread is start");
ThreadImpl impl =new ThreadImpl("child thread");
impl.start();
//sleep 方法:导致调用此方法的线程暂停指定时间。
Thread.sleep(2000);
System.out.println("main thread is end");
}
}
运行结果:
current thread name is:child thread,vuale is :0
current thread name is:child thread,vuale is :1
current thread name is:child thread,vuale is :2
current thread name is:child thread,vuale is :3
current thread name is:child thread,vuale is :4
current thread name is:child thread,vuale is :5
current thread name is:child thread,vuale is :6
current thread name is:child thread,vuale is :7
current thread name is:child thread,vuale is :8
current thread name is:child thread,vuale is :9
main thread is end
二、拓展java.lang.Runnable 接口
package com.thread.demo;
public class ThreadImplSecond implements Runnable {
private String name;
public ThreadImplSecond(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println("current thread name is:" + getName() + ",vuale is :" + i);
}
}
}
package com.demo.test;
import com.thread.demo.ThreadImplSecond;
public class ThreadImplSecondTest {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.out.println("main thread is start");
Thread thread = new Thread(new ThreadImplSecond("child thread"));
thread.start();
//yield 方法:让出CPU,主线程进入就绪状态(CPU调度机制会重新扫描,并继续执行以下的相关代码)。
Thread.currentThread().yield();
System.out.println("main thread name is :"+Thread.currentThread().getName());
System.out.println("main thread is end");
}
}
输出结果:
main thread name is :main
main thread is end
current thread name is:child thread,vuale is :0
current thread name is:child thread,vuale is :1
current thread name is:child thread,vuale is :2
current thread name is:child thread,vuale is :3
current thread name is:child thread,vuale is :4
current thread name is:child thread,vuale is :5
current thread name is:child thread,vuale is :6
current thread name is:child thread,vuale is :7
current thread name is:child thread,vuale is :8
current thread name is:child thread,vuale is :9
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
重点提示:
提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
四、线程状态转换
五、线程调度
线程的调度
package com.demo.test;
import com.thread.demo.ThreadImplSecond;
public class ThreadImplSecondTest {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.out.println("main thread is start");
Thread thread = new Thread(new ThreadImplSecond("child thread"));
thread.start();
//yield 方法:让出CPU,主线程进入就绪状态(CPU调度机制会重新扫描,并继续执行以下的相关代码)。
Thread.currentThread().yield();
System.out.println("main thread name is :"+Thread.currentThread().getName());
System.out.println("main thread is end");
}
}
为什么要用join方法
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
try {
mTh1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
mTh2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
sleep()和yield()的区别
sleep()和yield()的区别):sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把 CPU 的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程
另外,sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权。在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。
单单在概念上理解清楚了还不够,需要在实际的例子中进行测试才能更好的理解。对Object.wait(),Object.notify()的应用最经典的例子,应该是三线程打印ABC的问题了吧,这是一道比较经典的面试题,题目要求如下:
建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。代码如下:
/**
* wait用法
* @author DreamSea
* @time 2015.3.9
*/
package com.multithread.wait;
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //确保按顺序A、B、C执行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
输出结果:
ABCABCABCABCABCABCABCABCABCABC
wait和sleep区别
共同点:
1. 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
2. wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep /join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。
不同点:
1. Thread类的方法:sleep(),yield()等
Object的方法:wait()和notify()等
2. 每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。
sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4. sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
所以sleep()和wait()方法的最大区别是:
sleep()睡眠时,保持对象锁,仍然占有该锁;
package com.demo.test;
import com.thread.demo.ThreadImplSecond;
public class ThreadImplThreeTest {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.out.println("main thread is start");
Thread main = Thread.currentThread();
//为当前主线程上锁
synchronized (main) {
Thread.sleep(1000);
//同步代码块中,启动子线程
Thread thread = new Thread(new ThreadImplSecond("child thread"));
thread.start();
}
System.out.println("main thread is end");
}
//通过上述代码得出一个结论:线程调用sleep 方法时,是不会释放对象锁,所以,我们一般不在同步方法或者是同步代码块中使用sleep 方法。
}
而wait()睡眠时,释放对象锁。
但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。