java多线程详解

来源:互联网 发布:云计算应用实例 编辑:程序博客网 时间:2024/05/17 01:08
一、简单线程的实现
1、Runnable接口实现多线陈
     1.1、将任务代码移到实现了Runnable接口的类的run方法中。这个接口非常简单,只有一个方法

public interface Runnable
     void run();
可以如下所示实现一个类:

class MyRunnable implements Runnable
     public void run(){
          do something
     }

     1.2、创建一个类对象:

Runnable r = new MyRunnable();

1.3、由Runnable创建一个Thread对象:

Thread t = new Thread(r);

1.4、启动线程

t.start();

2.继承Thread类实现多线程
     2.1、继承Thread类并重写run方法,将任务代码写进run方法中
- public class MyThread extends Thread {
-   public void run() {
-    System.out.println("MyThread.run()");
-   }
- }
     2.2、启动线程
     
- MyThread myThread1 = new MyThread();
- MyThread myThread2 = new MyThread();
- myThread1.start();
- myThread2.start();

二、Thread和Runnable的区别 
     其实Thread的run方法也是调用的Runnable的run方法,但是他们有什么区别呢?
   1、  如果一个类继承了Thread,ze不适合资源共享。但如果实现了Runnable接口的话,则容易实现资源共享。
class hello extends Thread {

    public void run() {

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

            if (count > 0) {

                System.out.println("count= " + count--);
            }
        }
    }

    public static void main(String[] args) {

        hello h1 = new hello();

        hello h2 = new hello();

        hello h3 = new hello();
        h1.start();
        h2.start();
        h3.start();
    }

    private int count = 5;
}

     

【运行结果】:

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1
如果这是一个买票系统的话,count为剩余的车票数,上面的h1,h2,h3为三个人同时买票,那么就会出现问题了。

下面我们把Thread换为Runnable
class MyThread implements Runnable{

    private int ticket = 5;  //5张票

    public void run() {

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

            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);
            }
        }
    }
}

public class lzwCode {



    public static void main(String [] args) {

        MyThread my = new MyThread();

        new Thread(my, "1号窗口").start();

        new Thread(my, "2号窗口").start();

        new Thread(my, "3号窗口").start();
    }
}

【运行结果】:

count= 5

count= 4

count= 3

count= 2

count= 1

2、实现Runnble接口比继承Thread类所具有的优势
     2.1 适合多个相同的代码线程去处理同一个资源
     2.1可以避免java中的多线程限制
     2.3 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
所以建议尽量用接口来实现多线程。

注:
     提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
     在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。


三、线程的一些常用方法
     
3.1 设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。优先级代表的是概率,并不是绝对的优先级。例如:
     
  Thread t = new MyThread();
        t.setPriority(8);
        t.start();

     线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。
 
     线程默认优先级是5Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY 
          线程可以具有的最高优先级。
static int MIN_PRIORITY 
          线程可以具有的最低优先级。
static int NORM_PRIORITY 
          分配给线程的默认优先级。
 
3.2Thread.yield()方法
 
     Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
 
3.3、join()方法
 
     Thread的非静态方法join()让一个线程B“加入到另外一个线程A的尾部。在A执行完毕之前,B不能工作。例如:
     
   Thread t = new MyThread();
        t.start();
        t.join();

另外,join()方法还有带超时限制的重载版本。例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。

四、同步
     同步又称为并发,即多个线程访问同一个资源,要确保资源安全,即线程安全。
     多个线程同时访问一个资源,会造成一些问题。比如还是买车票的问题,如果在一个时间点上,两个线程同时使用这个资源,那他们取出的火车票是一样的(座位号一样),这样就会给乘客造成麻烦。

class MyThread implements Runnable{

    private int ticket = 10;  //5张票

    public void run() {

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

            if (this .ticket > 0) {
                System. out .println(Thread.currentThread().getName()+ "正在卖票" + this. ticket--);
            }
        }
    }

    public static void main(String [] args) {

        MyThread my = new MyThread();

        new Thread(my , "1号窗口" ).start();

        new Thread(my , "2号窗口" ).start();

        new Thread(my , "3号窗口" ).start();
    }
}

运行结果


可以看出上面的代码出现了问题,那么怎么解决这个问题呢?且看下面分解:

     1、同步块
          即在run方法中假如synchronized块,来保证每一次只有一个线程进来。
          

class MyThread implements Runnable{

    private int ticket = 10;  //5张票

    public void run() {

        for (int i =0; i<=20; i++) {
             synchronized (this ){
            if (this .ticket > 0) {
                   try {
                              Thread. sleep(500);
                        } catch (Exception e ) {
                               e.printStackTrace();
                        }
                System. out .println(Thread.currentThread().getName()+ "正在卖票" + this. ticket--);
            }
        }
        }
    }

    public static void main(String [] args) {

        MyThread my = new MyThread();

        new Thread(my , "1号窗口" ).start();

        new Thread(my , "2号窗口" ).start();

        new Thread(my , "3号窗口" ).start();
    }
}
运行结果:可以看出结果正常。

     2、同步方法
          从1.0版本开始,java的每个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。
      
    public synchronized void method(){
               //do something
          }
买票代码改进如下

class MyThread implements Runnable{

    private int ticket = 10;  //10张票

    public void run() {

        for (int i =0; i<=20; i++) {
             if (this .ticket > 0) {
                   try {
                              Thread. sleep(500);
                        } catch (Exception e ) {
                               e.printStackTrace();
                        }
            }
            sale();
    }
   }
        public synchronized void sale(){
         if (this .ticket > 0) {

               System. out .println(Thread.currentThread().getName()+ "正在卖票" + this. ticket--);
           }

    }

    public static void main(String [] args) {

        MyThread my = new MyThread();

        new Thread(my , "1号窗口" ).start();

        new Thread(my , "2号窗口" ).start();

        new Thread(my , "3号窗口" ).start();
    }
}
显示结果

     3、死锁
          过多的同步容易造成死锁
           是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
              死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。
         下面从网上找了一个图,很形象的说明了此问题


package com.slowly.tongbu;

class Zhangsan{ // 定义张三类
       public void say(){
      System. out .println("张三对李四说:“你给我画,我就把书给你。”" ) ;
      }
       public void get(){
            System. out .println("张三得到画了。" ) ;
      }
}
class Lisi{ // 定义李四类
       public void say(){
      System. out .println("李四对张三说:“你给我书,我就把画给你”" ) ;
      }
       public void get(){
            System. out .println("李四得到书了。" ) ;
      }
}
public class ThreadDeadLock implements Runnable{
       private static Zhangsan zs = new Zhangsan() ; // 实例化static型对象
       private static Lisi ls = new Lisi() ; // 实例化static型对象
       private boolean flag = false ; // 声明标志位,判断那个先说话
       public void run(){ // 覆写run()方法
             if (flag ){
                   synchronized (zs ){ // 同步张三
                         zs .say() ;
                         try {
                              Thread. sleep(500) ;
                        } catch (InterruptedException e ){
                               e.printStackTrace() ;
                        }
                         synchronized (ls ){
                               zs .get() ;
                        }
                  }
            }
             else {
                   synchronized (ls ){
                         ls .say() ;
                         try {
                              Thread. sleep(500) ;
                        } catch (InterruptedException e ){
                               e.printStackTrace() ;
                        }
                         synchronized (zs ){
                         ls .get() ;
                        }
                  }
            }
      }
public static void main(String args[]){
      ThreadDeadLock t1 = new ThreadDeadLock() ; // 控制张三
      ThreadDeadLock t2 = new ThreadDeadLock() ; // 控制李四
       t1. flag = true ;
       t2. flag = false ;
      Thread thA = new Thread( t1) ;
      Thread thB = new Thread( t2) ;
       //两个线程启动,各自调用run方法,这样的话各自需要的资源被占用,死锁发生
       thA .start() ;
       thB .start() ;
      }
}

声明:此文参考了很多博文,包括书籍,因为比较杂,本人已经记不住出处了,所以有些引用就不注明出处了,还请见谅。
0 0