关乎Java多线程+Runnable和Thread…

关乎Java多线程+Runnable和Thread的区别



两种方法

        一种是继承自Thread类.Thread  类是一个具体的类,即不是抽象类,该类封装了线程的行为.要创建一个线程,程序员必须创建一 个从  Thread  类导出的新类.程序员通过覆盖  Thread  的  run()  函数来完成有用的工作.用户并不直接调用此函数;而是通过调 用  Thread  的  start()  函数,该函数再调用  run().
       
        例如: 

        public  class  Test  extends  Thread{
            public  Test(){
            }
            public  static  void  main(String  args[]){
                Test  t1  new  Test();
                Test  t2  new  Test();
                t1.start();
                t2.start();
            }
            public  void  run(){
                //do  thread's  things
            }
        }



--------------------------------------------------------------------------------

       
        另一种是实现Runnable接口,此接口只有一个函数,run(),此函数必须由实现了此接口的类实现.
       
        例如: 

        public  class  Test  implements  Runnable{
            Thread  thread1;
            Thread  thread2;
            public  Test(){
                thread1  new  Thread(this,"1");
                thread2  new  Thread(this,"2");
            }
            public  static  void  main(String  args[]){
                Test  new  Test();
                t.startThreads();
            }
            public  void  run(){
                //do  thread's  things
            }
            public  void  startThreads(){
                thread1.start();
                thread2.start();
            }
        }

        两种创建方式看起来差别不大,但是弄不清楚的话,也许会将你的程序弄得一团糟.两者区别有以下几点:

1.当你想继承某一其它类时,你只能用后一种方式.

2.第一种因为继承自Thread,只创建了自身对象,但是在数量上,需要几个线程,就得创建几个自身对象;第二种只创建一个自身对象,却创建几 个Thread对象.而两种方法重大的区别就在于此,请你考虑:如果你在第一种里创建数个自身对象并且start()后,你会发现好像 synchronized不起作用了,已经加锁的代码块或者方法居然同时可以有几个线程进去,而且同样一个变量,居然可以有好几个线程同时可以去更改它. (例如下面的代码)这是因为,在这个程序中,虽然你起了数个线程,可是你也创建了数个对象,而且,每个线程对应了每个对象也就是说,每个线程更改和占有的 对象都不一样,所以就出现了同时有几个线程进入一个方法的现象,其实,那也不是一个方法,而是不同对象的相同的方法.所以,这时候你要加锁的话,只能将方 法或者变量声明为静态,将static加上后,你就会发现,线程又能管住方法了,同时不可能有两个线程进入同样一个方法,那是因为,现在不是每个对象都拥 有一个方法了,而是所有的对象共同拥有一个方法,这个方法就是静态方法.

        而你如果用第二种方法使用线程的话,就不会有上述的情况,因为此时,你只创建了一个自身对象,所以,自身对象的属性和方法对于线程来说是共有的.

        因此,我建议,最好用后一种方法来使用线程.

public  class  mainThread  extends  Thread{
    int  i=0;
    public  static  void  main(String  args[]){
        mainThread  m1  new  mainThread();
        mainThread  m2  new  mainThread();
        mainThread  m3  new  mainThread();
        mainThread  m4  new  mainThread();
        mainThread  m5  new  mainThread();
        mainThread  m6  new  mainThread();
        m1.start();
        m2.start();
        m3.start();
        m4.start();
        m5.start();
        m6.start();
    }
    public  synchronized  void  t1(){
        i=++i;
        try{

        }
        catch(Exception  e){}
        //每个线程都进入各自的t1()方法,分别打印各自的i
        System.out.println(Thread.currentThread().getName()+"  "+i);
    }
    public  void  run(){
        synchronized(this){
            while  (true)  {
                t1();
            }
        }
    }
}




--------------------------------------------------------------------------------



        下面我们来讲synchronized的4种用法吧:

        1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.即一次只能有一个线程进入该方法,其他线 程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.
 
            例如:

            public  synchronized  void  synMethod()  {
                //方法体
            }

        2.对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块.例如:

            public  int  synMethod(int  a1){
                synchronized(a1)  {
                    //一次只能有一个线程进入
                }
            }
        3.synchronized后面括号里是一对象,此时,线程获得的是对象锁.例如:

public  class  MyThread  implements  Runnable  {
    public  static  void  main(String  args[])  {
        MyThread  mt  new  MyThread();
        Thread  t1  new  Thread(mt,  "t1");
        Thread  t2  new  Thread(mt,  "t2");
        Thread  t3  new  Thread(mt,  "t3");
        Thread  t4  new  Thread(mt,  "t4");
        Thread  t5  new  Thread(mt,  "t5");
        Thread  t6  new  Thread(mt,  "t6");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }

    public  void  run()  {
        synchronized  (this)  {
            System.out.println(Thread.currentThread().getName());
        }
    }



 
        对于3,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法.为 什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一 部分资源,就将所有线程都锁在外面.由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:

class  FineGrainLock  {

      MyMemberClass  x,  y;
      Object  xlock  new  Object(),  ylock  new  Object();

      public  void  foo()  {
            synchronized(xlock)  {
                  //access  here
            }

            //do  something  here  but  don't  use  shared  resources

            synchronized(ylock)  {
                  //access  here
            }
      }

      public  void  bar()  {
            synchronized(this)  {
                  //access  both  and  here
            }
            //do  something  here  but  don't  use  shared  resources
      }
}

 

        4.synchronized后面括号里是类.例如:

class  ArrayWithLockOrder{
    private  static  long  num_locks  0;
    private  long  lock_order;
    private  int[]  arr;

    public  ArrayWithLockOrder(int[]  a)
    {
        arr  a;
        synchronized(ArrayWithLockOrder.class)  {//-----------------------------------------这里
            num_locks++;                          //  锁数加  1.
            lock_order  num_locks;    //  为此对象实例设置唯一的  lock_order.
        }
    }
    public  long  lockOrder()
    {
        return  lock_order;
    }
    public  int[]  array()
    {
        return  arr;
    }
}

class  SomeClass  implements  Runnable
  TestThread  testthread2  new  TestThread(this,  "2");

        testthread2.start();
        testthread1.start();

    }

    public  static  void  main(String[]  args)  {
        DemoThread  demoThread1  new  DemoThread();

    }

    public  void  run()  {

        TestThread  (TestThread)  Thread.currentThread();
        try  {
            if  (!t.getName().equalsIgnoreCase("1"))  {
                synchronized  (this)  {
                    wait();
                }
            }
            while  (true)  {

                System.out.println("@time  in  thread"  t.getName()  "="  +
                                                      t.increaseTime());

                if  (t.getTime()  10  ==  0)  {
                    synchronized  (this)  {
                        System.out.println("****************************************");
                        notify();
                        if  (t.getTime()  ==  100)
                            break;
                        wait();
                    }
                }
            }
        }
        catch  (Exception  e)  {
            e.printStackTrace();
        }
    }

}

class  TestThread
        extends  Thread  {
    private  int  time  0;
    public  TestThread(Runnable  r,  String  name)  {
        super(r,  name);
    }

    public  int  getTime()  {
        return  time;
    }

    public  int  increaseTime()  {
        return++time;
    }

}

        下面我们用生产者/消费者这个例子来说明他们之间的关系:

        public  class  test  {
    public  static  void  main(String  args[])  {
        Semaphore  new  Semaphore(1);
        Thread  t1  new  Thread(s,  "producer1");
        Thread  t2  new  Thread(s,  "producer2");
        Thread  t3  new  Thread(s,  "producer3");
        Thread  t4  new  Thread(s,  "consumer1");
        Thread  t5  new  Thread(s,  "consumer2");
        Thread  t6  new  Thread(s,  "consumer3");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

class  Semaphore
        implements  Runnable  {
    private  int  count;
    public  Semaphore(int  n)  {
        this.count  n;
    }

    public  synchronized  void  acquire()  {
        while  (count  ==  0)  {
            try  {
                wait();
            }
            catch  (InterruptedException  e)  {
                //keep  trying
            }
        }
        count--;
    }

    public  synchronized  void  release()  {
        while  (count  ==  10)  {
            try  {
                wait();
            }
            catch  (InterruptedException  e)  {
                //keep  trying
            }
        }
        count++;
        notifyAll();  //alert  thread  that's  blocking  on  this  semaphore
    }

    public  void  run()  {
        while  (true)  {
            if  (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("consumer"))  {
                acquire();
            }
            else  if  (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("producer"))  {
                release();
            }
            System.out.println(Thread.currentThread().getName()  count);
        }
    }
}

              生产者生产,消费者消费,一般没有冲突,但当库存为0时,消费者要消费是不行的,但当库存为上限(这里是10)时,生产者也不能生产.请好好研读上面的程序,你一定会比以前进步很多.

            上面的代码说明了synchronized和wait,notify没有绝对的关系,在synchronized声明的方法,代码块 中,你完全可以不用wait,notify等方法,但是,如果当线程对某一资源存在某种争用的情况下,你必须适时得将线程放入等待或者唤醒.


---------------------------------------------------------------------------------------------------------------------------------------
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
package org.thread.demo;
class MyThread extends Thread{
private String name;
public MyThread(String name) {
super ();
this .name = name;
}
public void run(){
for ( int i= 0 ;i< 10 ;i++){
System.out.println( "线程开始:" + this .name+ ",i=" +i);
}
}
}
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1= new MyThread( "线程a" );
MyThread mt2= new MyThread( "线程b" );

mt1.run();
mt2.run();
}
}
但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。下面启动start()方法启动线程:

package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1= new MyThread( "线程a" );
MyThread mt2= new MyThread( "线程b" );
mt1.start();
mt2.start();
}
};
这 样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?在JDK的安装路径下,src.zip是全部的java源程序,通过 此代码找到Thread中的start()方法的定义,可以发现此方法中使用了private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)
Runnable接口
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。

public interface Runnable{
public void run();
}
例子:

package org.runnable.demo;
class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this .name = name;
}
public void run(){
for ( int i= 0 ;i< 100 ;i++){
System.out.println( "线程开始:" + this .name+ ",i=" +i);
}
}
};
但 是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协 调系统的资源):

package org.runnable.demo;
import org.runnable.demo.MyThread;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1= new MyThread( "线程a" );
MyThread mt2= new MyThread( "线程b" );
new Thread(mt1).start();
new Thread(mt2).start();
}
}

两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:

避免点继承的局限,一个类可以继承多个接口。
适合于资源的共享


以卖票程序为例,通过Thread类完成:

package org.demo.dff;
class MyThread extends Thread{
private int ticket= 10 ;
public void run(){
for ( int i= 0 ;i< 20 ;i++){
if ( this .ticket> 0 ){
System.out.println( "卖票:ticket" + this .ticket--);
}
}
}
};
下面通过三个线程对象,同时卖票:

package org.demo.dff;
public class ThreadTicket {
public static void main(String[] args) {
MyThread mt1= new MyThread();
MyThread mt2= new MyThread();
MyThread mt3= new MyThread();
mt1.start(); //每个线程都各卖了10张,共卖了30张票
mt2.start(); //但实际只有10张票,每个线程都卖自己的票
mt3.start(); //没有达到资源共享
}
}

如果用Runnable就可以实现资源共享,下面看例子:

package org.demo.runnable;
class MyThread implements Runnable{
private int ticket= 10 ;
public void run(){
for ( int i= 0 ;i< 20 ;i++){
if ( this .ticket> 0 ){
System.out.println( "卖票:ticket" + this .ticket--);
}
}
}
}
package org.demo.runnable;
public class RunnableTicket {
public static void main(String[] args) {
MyThread mt= new MyThread();
new Thread(mt).start(); //同一个mt,但是在Thread中就不可以,如果用同一
new Thread(mt).start(); //个实例化对象mt,就会出现异常
new Thread(mt).start();
}
};
虽然现在程序中有三个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。
Runnable接口和Thread之间的联系: public class Thread extends Object implements Runnable 发现Thread类也是Runnable接口的子类。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值