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()+"释放了锁");        }    }}



1 0
原创粉丝点击