7、多线程
来源:互联网 发布:约瑟夫环 知乎 编辑:程序博客网 时间:2024/06/09 21:48
wait, notify参考:点击打开链接
sleep, join参考:点击打开链接
1.Java中线程的状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead),注意blocked、waiting、time waiting也可以统称为阻塞状态,这样可以将线程的状态和Java中的方法调用联系起来,所以将waiting和time waiting两个状态分离出来。线程状态转换图:
2. 上下文切换:线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据,实际上就是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。
3. Java中实现多线程有两种手段,一种是继承Thread,另一种是实现Runnable:
1) 继承Thread:
public classwelcome { publicstatic void main(String[] args) { //MyThread t1 = newMyThread("A"); //MyThread t2 = newMyThread("B"); //t1.start(); //t2.start(); MyThread t = newMyThread(); newThread(t,"A").start(); newThread(t,"B").start(); }} class MyThread extendsThread{ publicMyThread(String name) { this.name = name; } publicMyThread(){} publicvoid run(){ for(int index= 0; index < 5; ++index) { System.out.println(name+"Running:"+index); try{ sleep((int)Math.random()*10); }catch(InterruptedException e) { e.printStackTrace(); } } } privateString name; }
从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。
2) 实现Runnable:
public classwelcome { publicstatic void main(String[] args) { MyThread2my = newMyThread2(); new Thread(my,"C").start(); newThread(my,"D").start();}class MyThread2 implementsRunnable{ @Override publicvoid run() { for(int index= 0; index < 5; ++index) { System.out.println(Thread.currentThread().getName()+" Running: " + count--); try{ Thread.sleep((int)Math.random()*10); }catch(InterruptedException e) { e.printStackTrace(); } } } private int count= 15;}
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。这里要注意每个线程都是用同一个实例化对象,如果不是同一个,效果就和上面的一样了!
3) 实现Runnable接口比继承Thread类所具有的优势:a、适合多个相同的程序代码的线程去处理同一个资源;b、可以避免java的单继承限制;c、增加程序的健壮性,代码可以被多线程共享,代码和数据独立
4. java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁,或者叫做监视器(monitor)。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。
5. synchronized关键字:参考:点击打开链接
1) synchronized关键字修饰方法,该方法叫同步方法:当一个线程访问某个对象synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法)
2) 静态同步方法:当一个synchronized关键字修饰的方法同时又被static修饰,之前说过,非静态的同步方法会将对象上锁,但是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁。
3) synchronized块:synchronized(object){},表示线程在执行的时候会将object对象上锁(注意这个对象可以是任意类的对象,也可以使用this关键字)
4) 总结:synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。
6. 调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:MAX_PRIORITY(10)、MIN_PRIORITY(1)、NORM_PRIORITY(5),主线程的默认优先级为Thread.NORM_PRIORITY,线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
7. 线程睡眠:Thread.sleep(longmillis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。
public class Test { private int i = 10; private Object object = new Object(); public static void main(String[] args) throws IOException { Test test = new Test(); MyThread thread1 = test.new MyThread(); MyThread thread2 = test.new MyThread(); thread1.start(); thread2.start(); } class MyThread extends Thread{ @Override public void run() { synchronized (object) { i++; System.out.println("i:"+i); try { System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态"); Thread.currentThread().sleep(10000); } catch (InterruptedException e) { // TODO: handle exception } System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束"); i++; System.out.println("i:"+i); } } }}
8. 线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,sleep 方法允许较低优先级的线程获得运行机会,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行
9. 线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
public class Test { public static void main(String[] args) throws IOException { System.out.println("进入线程"+Thread.currentThread().getName()); Test test = new Test(); MyThread thread1 = test.new MyThread(); thread1.start(); try { System.out.println("线程"+Thread.currentThread().getName()+"等待"); thread1.join(); System.out.println("线程"+Thread.currentThread().getName()+"继续执行"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } class MyThread extends Thread{ @Override public void run() { System.out.println("进入线程"+Thread.currentThread().getName()); try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { // TODO: handle exception } System.out.println("线程"+Thread.currentThread().getName()+"执行完毕"); } }}
10. 线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。(wait()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常)
1) wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2) 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monito (即锁)
3) 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4) 调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
public class Test { public static Object object = new Object(); public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); } static class Thread1 extends Thread{ @Override public void run() { synchronized (object) { try { object.wait(); } catch (InterruptedException e) { } System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁"); } } } static class Thread2 extends Thread{ @Override public void run() { synchronized (object) { object.notify(); System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()"); } System.out.println("线程"+Thread.currentThread().getName()+"释放了锁"); } }}
- 7、多线程
- 多线程(7):
- Linux多线程实践(7) --多线程排序对比
- 2011-9-7多线程
- 9-7多线程
- Java多线程 7 死锁
- (7)Java多线程
- 多线程-7-线程池
- 黑马程序员--7--多线程
- Java多线程7:死锁
- 多线程-7、dispatch对象
- 多线程(7)
- java多线程[7]:CyclicBarrier
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- excel导出导出
- 根目录下常用目录作用
- java的List集合
- 孤儿进程与僵尸进程
- javascript变量声明提升(hoisting)
- 7、多线程
- 数据结构实验之二叉树五:层序遍历
- Codeforces 543A. Writing Code DP
- 线性代数
- Linux编译安装中configure、make和make install各自的作用
- NSJSONSerialization-JSON数据与NSDictionary和NSArray之间的转化
- 28. SpringBoot启动时的Banner设置【从零开始学Spring Boot】
- 类别(Category)的作用(一)---将实现分散到不同文件
- Android 调用系统内置分享