Java基础——多线程

线程的基本概念

 

线程是一个程序里面不同的执行路径,是一个程序内部的顺序控制流。

³     线程和进程的区别

²     每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。

²     线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。

²     多进程: 在操作系统中能同时运行多个任务(程序)

²     多线程: 在同一应用程序中有多个顺序流同时执行

 

线程的创建和启动

³     可以有两种方式创建新的线程。

²     第一种

±     定义线程类实现Runnable接口

±     Thread myThread new Theadtarget//targetRunnable接口类型。

±     Runnable中只有一个方法:

°     public void run(); 用以定义线程运行体。

±     使用Runnable接口可以为多个线程提供共享的数据。

±     在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:

°     public static Thread currentThread()  获取当前线程的引用。

²     第二种

±     可以定义一个Thread的子类并重写其run方法如:

      class MyThread extends Thead {

           public void run(){…}

       }

±     然后生成该类的对象:

                    MyThread myThreadnew MyThead(…)

³     使用那种好呢?推荐使用接口,因为使用继承之后就不能继承其它的类了,比较死。使用接口比较灵活(可以继续实现其它类或者接口)

Eg

publicstaticvoid main(String[] args) {

      // TODO Auto-generated method stub

      Thread1 r=new Thread1();

      r.run();//方法调用

      Thread th=new Thread(r);

      th.start();//线程启动

   

      Thread2 r2=new Thread2();

      r2.start();

      

      for (int i = 0; i <100; i++) {

         System.out.println("Main Thread---"+i);

      }
   }
}

class Thread1 implements Runnable {
   @Override
   publicvoid run() {
      // TODO Auto-generated method stub
      for (int i = 0; i <100; i++) {
         System.out.println("Thread1---"+i);
      }
   }
}
class Thread2 extends Thread{
   publicvoid run() {
      // TODO Auto-generated method stub
      for (int i = 0; i <100; i++) {
         System.out.println("Thread2---"+i);
      }
   }
}


 

线程状态转换

线程控制基本方法

 

       

isAlive()

判断线程是否还着,即线程是否还未终止。

getPriority()

获得线程的优先级数值

setPriority()

设置线程的优先级数值

Thread.sleep()

将当前线程睡眠指定毫秒数

join()

调用某线程的该方法,将当前线程与该线程合并,即等待该线程结束,再恢复当前线程的运行。

yield()

让出CPU,当前线程进入就绪队列等待调度。

wait()

当前线程进入对象的wait pool

notify()/

notifyAll()

唤醒对象的wait pool中的一个/所有等待线程。

 

sleep / join / yield 方法

³     sleep方法:

²     可以调用Thread的静态方法:

      public static void sleep(long millis) throws InterruptedException

      使得当前线程休眠(暂时停止执行millis毫秒)。

²     由于是静态方法,sleep可以由类名直接调用:

         Thread.sleep(…)

³     join方法:

²     合并某个线程,等待该合并的线程终止才会继续执行当前的线程。

³     yield方法:

²     让出CPU,给其他线程执行的机会,暂停当前正在执行的线程对象,并执行其他线程。只会暂停一下。

Eg

publicclass TestInterrupt {

 

   /**

    * @param args

    */

   publicstaticvoid main(String[] args) {

      // TODO Auto-generated method stub

      Mythread th=new Mythread();

      th.start();

      try {

         Thread.sleep(10000);

      } catch (InterruptedException e) {

         // TODO Auto-generated catch block

         e.printStackTrace();

      }

      //th.interrupt();//可以终止子线程。并不是终止子线程的最好的方法。

      //更加不要使用stop方法来终止线程,stop方法更加的粗暴。

      th.flag =false;//使用这种方法来终止子线程比较合适

   }

 

}

class Mythread extends Thread{

   booleanflag=true;

   publicvoid run() {

      while(flag){

         System.out.println(new Date());

         try {

            sleep(1000);

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            return;

         }

      }

   }

}


 

Eg2

publicclass TestJoin {

   /**

    * @param args

    */

   publicstaticvoid main(String[] args) {

      // TODO Auto-generated method stub

      MyThread2 th=new MyThread2("child Thread");

      th.start();

      try {

         th.join();//将th线程合并到当前线程中等待th线程执行完后才会执行当前线程。

      } catch (InterruptedException e) {

         // TODO Auto-generated catch block

         e.printStackTrace();

      }

      for(int i=0;i<10;i++){

         System.out.println("I am Main Thread");

      }

 

   }

 

}

class MyThread2 extends Thread{

   public MyThread2(String s) {

      // TODO Auto-generated constructor stub

      super(s);

   }

   publicvoid run(){

      for(int i=0;i<10;i++){

         try {

            sleep(1000);

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            return;

         }

         System.out.println("I am "+getName());

      }

   }

}

 


Eg3

publicclass TestYield {

 

   /**

    * @param args

    */

   publicstaticvoid main(String[] args) {

      // TODO Auto-generated method stub

      MyThread3 th1=new MyThread3("th1");

      MyThread3 th2=new MyThread3("th2");

      th1.start();

      th2.start();

   }

 

}

class MyThread3 extends Thread{

   public MyThread3(String s) {

      super(s);

   }

   publicvoid run() {

      for(int i=0;i<=100;i++){

         System.out.println(getName()+"  "+i);

         if(i%10==0){

            yield();

         }

      }

   }

   

}


 

线程的优先级别

³     Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。

³      线程的优先级用数字表示,范围从110,一个线程的缺省优先级是5

²                Thread.MIN_PRIORITY = 1

²                Thread.MAX_PRIORITY = 10

²                Thread.NORM_PRIORITY = 5

³      使用下述线方法可以获得或设置线程对象的优先级。

²                int getPriority();

²                void setPriority(int newPriority);

³     不同平台上的优先级

²     Solaris:相同优先级的线程不能相互抢占对方的cpu时间。

²     windows:可以抢占相同甚至更高优先级的线程的cpu时间

Eg

publicclass TestPriority {

 

   /**

    * @param args

    */

   publicstaticvoid main(String[] args) {

      // TODO Auto-generated method stub

      Thread t1 = new Thread(new T1());

      Thread t2 = new Thread(new T2());

      t1.setPriority(Thread.NORM_PRIORITY+3);

      t1.start();

      t2.start();

   }

 

}

 

class T1 implements Runnable {

 

   @Override

   publicvoid run() {

      // TODO Auto-generated method stub

      for (int i = 0; i <= 1000; i++) {

         System.out.println("T1"+"   "+i);

      }

   }

 

}

 

class T2 implements Runnable {

 

   @Override

   publicvoid run() {

      // TODO Auto-generated method stub

      for (int i = 0; i <= 1000; i++) {

         System.out.println("---------T2"+"   "+i);

      }

   }

 

}


 

线程同步

³     Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应于一个可称为互斥锁的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。

³      关键字synchronized 来与对象的互斥锁联系。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。

 

Ø       synchronized 的使用方法:

synchronized(this){

  num ++;

  try {Thread.sleep(1);}

  catch (InterruptedException e) {}

  System.out.println

    (name+", 你是第"+num+"个使用timer的线程");

}

Ø       synchronized 还可以放在方法声明中,表示整个方法为同步方法,例如:

      synchronized public void add(String name){…}

 

Eg

publicclass TestSynchronised implements Runnable {

 

   /**

    * @param args

    */

   Timer timer=new Timer();

   publicstaticvoid main(String[] args) {

      // TODO Auto-generated method stub

      TestSynchronised test=new TestSynchronised();

      Thread t1=new Thread(test);

      Thread t2=new Thread(test);

      t1.setName("t1");

      t2.setName("t2");

      t1.start();

      t2.start();

   }

 

   @Override

   publicvoid run() {

      // TODO Auto-generated method stub

      timer.add(Thread.currentThread().getName());

   }

 

}

class Timer{

   privatestaticintnum=0;

   publicvoid add(String name){

      synchronized(this){

         num++;

         try {

            Thread.sleep(1);

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

         System.out.println(name+"你是第"+num+"个使用timer的线程");

      }     

   }

}


 

³     无论synchronized关键字加在方法上还是对象上,它取得的锁都是锁在了对象上,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

³     每个对象只有一个锁(lock)与之相关联。

³     实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

³     搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。

³     还有一些技巧可以让我们对共享资源的同步访问更加安全:

²     定义private instance变量+它的 get方法,而不要定义public/protectedinstance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。

²     如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。

 

 

死锁:简单的说就是有两个线程t1t2,这两个线程都需要锁住AB两个对象才能执行完,当t1锁住了At2锁住了B的时候,这个时候两个线程都无法继续执行,就会产生死锁。

Eg

publicclass TestDeadLock implements Runnable {

   publicintflag = 1;

   static Object o1 = new Object(), o2 = new Object();

 

   publicstaticvoid main(String[] args) {

      TestDeadLock td1 = new TestDeadLock();

      TestDeadLock td2 = new TestDeadLock();

      td1.flag = 1;

      td2.flag = 0;

      Thread t1 = new Thread(td1);

      Thread t2 = new Thread(td2);

      t1.start();

      t2.start();

 

   }

 

   publicvoid run() {

      System.out.println("flag=" + flag);

 

      if (flag == 1) {

         synchronized (o1) {

            try {

                Thread.sleep(500);

            } catch (Exception e) {

                e.printStackTrace();

            }

            synchronized (o2) {//锁住o1的同时还要锁住02(在o1的里面)。

                System.out.println("1");

            }

         }

      }

      if (flag == 0) {

         synchronized (o2) {

            try {

                Thread.sleep(500);

            } catch (Exception e) {

                e.printStackTrace();

            }

            synchronized (o1) {

                System.out.println("0");

            }

         }

      }

   }

}


 

Synchronized总结

³     无论synchronized关键字加在方法上还是对象上,它取得的锁都是锁在了对象上,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

³     每个对象只有一个锁(lock)与之相关联。

³     实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

³     搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。

³     还有一些技巧可以让我们对共享资源的同步访问更加安全:

²     定义private instance变量+它的 get方法,而不要定义public/protectedinstance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。

²     如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怎么演

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值