线程与进程

线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。

线程是程序内的顺序控制流,只能使用分配给程序的资源和环境。

 

多线程编程的目的,就是最大限度地利用CPU资源。

多线程:指的是在单个程序中可以同时运行多个不同的线程执行不同的任务。

 

 

进程:运行一个程序就会启动一个进程,本质是一个执行的程序。(程序是静态的,而进程是动态的)

 

单线程:当程序启动时,就自动产生一个线程,主方法main就在这个主线程上运行。

 

多线程

一个进程可以包含一个或多个线程

一个程序实现多个代码同时交替运行就需要产生多个线程

CPU随机的抽出时间,让我们的程序一会做这件事情,一会做另外一件事情。

 

 

进程与线程的区别

进程是重量级的任务,需要分配给它们独立的地址空间。进程间通信是昂贵的受限的

线程共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,转换也是低成本 的。

 

 

线程的实现:

第一种方式:继承Thread类民,然后重写run方法

public class ThreadTest

{

    public static void main(String[] args)

    {

       Thread1 t1 = new Thread1();

       Thread2 t2 = new Thread2();

      

       t1.start();

       t2.start();

    }

}

 

class Thread1 extends Thread

{

    @Override

    public void run()

    {

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

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

    }

}

 

class Thread2 extends Thread

{

    @Override

    public void run()

    {

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

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

    }

}

 

 

第二种方式:实现Runnable接口,然后实现run方法。

public class ThreadTest2

{

    public static void main(String[] args)

    {

       Thread t1 = new Thread(new MyThread1());

 

       t1.start();

 

       Thread t2 = new Thread(new MyThread2());

 

       t2.start();

 

       System.out.println(t1.getId() + t1.getName());

 

       System.out.println(t2.getId() + t2.getName());

    }

}

 

class MyThread1 implements Runnable

{

    @Override

    public void run()

    {

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

       {

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

       }

    }

}

 

class MyThread2 implements Runnable

{

    @Override

    public void run()

    {

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

       {

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

       }

    }

}

 

将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,strart方法首先为线程执行准备好系统资源中,然后再去调用run方法。

 

一个进程至少要有一个线程

 

对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。

 

对于双核或多核的CPU来说,可以真正做到微观并行。

 

Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法。

 

当生成一个线程对象时,如果没有为它设定名字,线程对象会使用如下形式取名:

Thread-numbernumber是自动增加的,并被所有的Thread对象所共享(因为它是Static的成员变量)

 

Thread类的run方法什么也没做,只有我们传入一个继承Thread类或实现Runnable接口的

类的实例时,它才具有意义。

也就是说Thread类的run方法中执行的代码,是我们编写的run方法代码。

 

线程的消亡不能通过调用一个stop()命令,而是让run方法自然结束。

一般用如下方式结束线程

public class MyThread implements Runnable

{

    private boolean flag = true;

    public void run()

    {

       while(flag)

       {

           if(...)

           {

              StopRunning();

           }

       }

    }

   

    public void StopRunning()

    {

       flag = false;

    }

}

 

 

线程的生命周期:一个线程从创建到消亡的过程。

1.创建状态

当用new操作符创建时

处于创建状态的线程只是一个空的线程对象,系统不为它分配资源。

 

2.可运行状态

执行线程的start方法,为线程分配必须的系统资源,安排其运行,并调用run方法,此时处于Runnable状态

这一状态并不是运行中状态(running),因为线程也许实际上并未真正运行。

 

3.不可运行状态

下列情况的发生,会使线程从运行状态转到不可运行状态:

调用了sleep方法

调用wait方法等待特定条件的满足。

线程输入/输出阻塞

 

返回可运行状态:

处于睡眠状态的线程在指定的时间过去后

调用notify()notifyAll方法通知等待线程条件的改变。

如果线程是因为输入/输出阻塞,等待输入/输出完成

 

4.消亡状态

Run方法执行结束后,该线程自然消亡。

 

 

线程的优先级可以用setPriority()方法来改变,一般是1-10之间的正整数。

 

线程的调度策略

线程调度器选择优先级最高的线程运行,但是,如果发生以下情况,就会终止线程的运行:

1.线程体中调用了yield方法,让出了对cpu的占用权

2.线程体中调用了sleep方法,使线程进入睡眠状态

3.线程由于I/O操作受阻塞

4.另一个更高优先级的线程实现

5.在支持时间片的系统中,该线程的时间片用完。

 

public class ThreadTest3

{

    public static void main(String[] args)

    {

       Runnable r = new HelloThread();

 

       Thread t1 = new Thread(r);

      

       r = new HelloThread();//引用的不是同一个对象,线程之间互不影响。

          

       Thread t2 = new Thread(r);

 

       t1.start();

       t2.start();

 

    }

}

 

class HelloThread implements Runnable

{

    int i;//当不同的线程引用同一个成员变量时,其中一个线程对它的修改会反映到其他线程上

 

    @Override

    public void run()

    {

       int i = 0;//当不同的线程对局部变量进行修改时,由于每个线程引用的只是它的一份拷贝,所以互不影响

      

       while (true)

       {

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

 

           try

           {

              Thread.sleep((long) (Math.random() * 1000));

           }

           catch (Exception e)

           {

              e.printStackTrace();

           }

 

           if (50 == i)

           {

              break;

           }

       }

    }

}

 

 

多线程的同步

 

1.为什么要引入同步机制

在多线程环境中,可能会出现两个或多个线程试图同时访问一个有限的资源。

必须对这种潜在的资源冲突进行预防

 

解决方法:在线程使用一个资源时为其加锁即可。这样其他线程就无法访问这个资源,直到锁被解开。

 

2.怎么实现同步

方法声明中加上synchronized关键字,当方法被调用时,对象就会被锁定。

说明:

synchronized方法执行完或发生异常时,会自动释放锁。

synchronized保护的数据应该是私有的

 

一个银行取钱的例子。

public class FetchMoney

{

    public static void main(String[] args)

    {

       Bank bank = new Bank();

 

       Thread t1 = new MoneyThread(bank);

       Thread t2 = new MoneyThread(bank);

 

       t1.start();

       t2.start();

    }

}

 

class Bank

{

    private int money = 1000;

 

    //synchronized关键字表示将对象上锁,只有在一个线程执行完毕后,另一线程才能去访问该对象。这样就避免了同时并发的发生。

    public synchronized int getMoney(int number)

    {

       if (number < 0)

       {

           return -1;

       }

       else if (number > money)

       {

           return -2;

       }

       else if (money < 0)

       {

           return -3;

       }

       else

       {

           try

           {

              Thread.sleep(1000);// 当让线程睡眠时,第一个线程暂时没起作用,那么第二个线程进来以后,前面的检查就跳过去了

           }

           catch (InterruptedException e)

           {

               e.printStackTrace();

           }

 

           money -= number;//线程同时醒过来,并起作用。出现同时取钱效果。

 

           System.out.println("Left money : " + money);

 

           return money;

       }

    }

}

 

class MoneyThread extends Thread

{

    private Bank bank;

 

    public MoneyThread(Bank bank)

    {

       this.bank = bank;

    }

 

    @Override

    public void run()

    {

       System.out.println(bank.getMoney(800));

    }

}

 

 

public class ThreadTest4

{

    public static void main(String[] args)

    {

       //有序执行和无序执行的关键:有几个对象?锁的是谁?

       Example e = new Example();

      

       Thread t1 = new TheThread(e);

       

       e = new Example();

      

       Thread t2 = new TheThread2(e);

      

       t1.start();

       t2.start();

    }

}

 

class Example

{

    //synchronized关键字表示将对象上锁,不管对象里有多少个synchronized修饰的方法,

    //都会先执行一个线程,这个线程执行完毕后,才执行第二个线程,也就是说不存在同时执行的可能。

    public synchronized void execute()

    {

       for (int i = 0; i < 20; i++)

       {

           try

           {

              Thread.sleep((long) (Math.random() * 500));

           }

           catch (InterruptedException e)

           {

              e.printStackTrace();

           }

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

       }

    }

   

    //当一个方法用synchronized static去修饰时,表示对对象所对应的class本身上锁。

    public synchronized static void execute2()

    {

       for (int i = 0; i < 20; i++)

       {

           try

           {

              Thread.sleep((long) (Math.random() * 500));

           }

           catch (InterruptedException e)

           {

              e.printStackTrace();

           }

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

       }

    }

}

 

class TheThread extends Thread

{

    private Example example;

 

    public TheThread(Example example)

    {

       super();

       this.example = example;

    }

 

    @Override

    public void run()

    {

       this.example.execute();

    }

}

 

class TheThread2 extends Thread

{

    private Example example;

 

    public TheThread2(Example example)

    {

       super();

       this.example = example;

    }

 

    @Override

    public void run()

    {

       this.example.execute2();

    }

}

 

使用synchronized{ }

public class ThreadTest5

{

    public static void main(String[] args)

    {

       // 有序执行和无序执行的关键:有几个对象?锁的是谁?

       Example1 e = new Example1();

 

       Thread t1 = new TheThread3(e);

 

       e = new Example1();

 

       Thread t2 = new TheThread4(e);

 

       t1.start();

       t2.start();

    }

}

 

class Example1

{

    public synchronized void execute()

    {

       //实际开发可以选择采用synchronized语句块,可以对线程的访问控制得更加细粒度

       synchronized (this)

       {

           for (int i = 0; i < 20; i++)

           {

              try

              {

                  Thread.sleep((long) (Math.random() * 500));

              }

              catch (InterruptedException e)

              {

                  e.printStackTrace();

              }

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

           }

       }

    }

 

    public void execute2()

    {

       synchronized (this)

       {

           for (int i = 0; i < 20; i++)

           {

              try

              {

                  Thread.sleep((long) (Math.random() * 500));

              }

              catch (InterruptedException e)

              {

                  e.printStackTrace();

              }

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

           }

       }

    }

}

 

class TheThread3 extends Thread

{

    private Example1 example;

 

    public TheThread3(Example1 example)

    {

       this.example = example;

    }

 

    @Override

    public void run()

    {

       this.example.execute();

    }

}

 

class TheThread4 extends Thread

{

    private Example1 example;

 

    public TheThread4(Example1 example)

    {

       this.example = example;

    }

 

    @Override

    public void run()

    {

       this.example.execute2();

    }

}

 

线程间的相互作用

 

使用Wait()notify()来控制线程间的相互作用,使它们相互协作,以防止死锁。

 

这两个方法都定义在Object,而且是final的,因此会被所有的java类所继承且不能被重写。

 

这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或块当中。

 

当线程执行了wait方法时,它会释放掉对象的锁。

 

Notify通知另外一个线程时是任意的,你无法决定它所通知的对象。

 

另一个会导致线程暂停的方法就是sleep方法,这会导致线程睡眠指定的毫秒数,但线程在睡眠过程中是不会释放掉对象的锁的。

 

Public class ThreadTest6

{

    public static void main(String[] args)

    {

       Sample sample = new Sample();

      

       Thread t1 = new IncreaseTeread(sample);

       Thread t2 = new DecreaseThread(sample);

      

       Thread t3 = new IncreaseTeread(sample);

       Thread t4 = new DecreaseThread(sample);

      

       t1.start();

       t2.start();

       t3.start();

       t4.start();

    }

}

 

class Sample

{

    private int number;

 

    public synchronized void increase() throws Exception

    {

    //这里要用while而不是if

       while (0 != number)

       {

           wait();

       }

 

       number++;

 

       System.out.println(number);

 

       notify();

    }

 

    public synchronized void decrease() throws Exception

    {

       while (0 == number)

       {

           wait();

       }

 

       number--;

 

       System.out.println(number);

 

       notify();

    }

}

 

class IncreaseTeread extends Thread

{

    private Sample sample;

 

    public IncreaseTeread(Sample sample)

    {

       super();

       this.sample = sample;

    }

 

    @Override

    public void run()

    {

       for (int i = 0; i < 20; i++)

       {

           try

           {

              Thread.sleep((long) (Math.random() * 1000));

 

              sample.increase();

           }

           catch (Exception e)

           {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

       }

    }

}

 

class DecreaseThread extends Thread

{

    private Sample sample;

 

    public DecreaseThread(Sample sample)

    {

       super();

       this.sample = sample;

    }

 

    @Override

    public void run()

    {

       for (int i = 0; i < 20; i++)

       {

           try

           {

              Thread.sleep((long) (Math.random() * 1000));

 

              sample.decrease();

           }

           catch (Exception e)

           {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

       }

    }

}

 

 

实际开发中如果遇到同时并发的问题,可以用java.util.concurrent包来进行处理。

 

Wait pool 和 lock pool


 

 

基于多线程的单件模式

public class Singleton

{

    private static Singleton instance = null;

    private static volatile Object objHelper = new Object();

 

    private Singleton()

    {

 

    }

 

    public static Singleton getInstance()

    {

       synchronized(objHelper)

       {

           while(null == instance)

           {

              try

              {

                  Thread.sleep((long)(Math.random()*3000));

              }

              catch (InterruptedException e)

              {

                  e.printStackTrace();

              }

             

              instance = new Singleton();

           }

       }

       return instance;

    }

 

    public static void main(String[] args)

    {

       new MyThread().start();

       new MyThread().start();

    }

}

 

class MyThread extends Thread

{

    @Override

    public void run()

    {

       System.out.println(Singleton.getInstance());

    }

}

 

posted @ 2011-08-17 21:58  水之原  阅读(263)  评论(0编辑  收藏  举报