`

java中用Runnable和Thread实现多线程,多线程间的同步和死锁。

    博客分类:
  • java
 
阅读更多

继承Thread实现多线程:

 

//MyThread类
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);  
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				System.out.println("线程出错"+this.name);
			}
		}  
	}  
} 
//ThreadDemo类
public class ThreadDemo {
	public static void main(String[] args) {  
		MyThread mt1=new MyThread("线程a");  
		MyThread mt2=new MyThread("线程b");  
		mt1.run();  
		mt2.run();  
		}  
}

此时实例化MyThread类,调用的是run()方法,此时执行的结果如下图:

 

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

 

//ThreadDemo类
public class ThreadDemo {
	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接口完成。

 

//MyThread类
class MyThread implements Runnable{  
	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);  
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				System.out.println("线程出错"+this.name);
			}
		}  
	} 

但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):

 

 

//Demo类
public class ThreadDemo {  
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类有如下好处:

 

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

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

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--);  
        }  
     }  
  }  
}; 

下面通过三个线程对象,同时卖票:

 

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就可以实现资源共享,下面看例子:

 

 

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--);  
         }  
      }  
   }  
}  
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接口的子类。

 

 

class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        new Thread(he,"A").start();
        new Thread(he,"B").start();
        new Thread(he).start();
    }
}

 

【运行结果】:

A

A

A

B

B

B

Thread-0

Thread-0

Thread-0

说明如果我们没有指定名字的话,系统自动提供名字。

判断线程是否启动

 

class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he);
        System.out.println("线程启动之前---》" + demo.isAlive());
        demo.start();
        System.out.println("线程启动之后---》" + demo.isAlive());
    }
}

 

【运行结果】

线程启动之前---》false

线程启动之后---》true

Thread-0

Thread-0

Thread-0

主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。

    /**
     * @author Rollen-Holt 线程的强制执行
     * */
    class hello implements Runnable {
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName());
            }
        }
     
        public static void main(String[] args) {
            hello he = new hello();
            Thread demo = new Thread(he,"线程");
            demo.start();
            for(int i=0;i<50;++i){
                if(i>10){
                    try{
                        demo.join();  //强制执行demo
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("main 线程执行-->"+i);
            }
        }
    }

【运行的结果】:

main 线程执行-->0

main 线程执行-->1

main 线程执行-->2

main 线程执行-->3

main 线程执行-->4

main 线程执行-->5

main 线程执行-->6

main 线程执行-->7

main 线程执行-->8

main 线程执行-->9

main 线程执行-->10

线程

线程

线程

main 线程执行-->11

main 线程执行-->12

main 线程执行-->13

...

/**
 * @author Rollen-Holt 线程的休眠
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "线程");
        demo.start();
    }
}

【运行结果】:(结果每隔2s输出一个)

线程0

线程1

线程2

/**
 * @author Rollen-Holt 线程的中断
 * */
class hello implements Runnable {
    public void run() {
        System.out.println("执行run方法");
        try {
            Thread.sleep(10000);
            System.out.println("线程完成休眠");
        } catch (Exception e) {
            System.out.println("休眠被打断");
            return;  //返回到程序的调用处
        }
        System.out.println("线程正常终止");
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "线程");
        demo.start();
        try{
            Thread.sleep(2000);
        }catch (Exception e) {
            e.printStackTrace();
        }
        demo.interrupt(); //2s后中断线程
    }
}

【运行结果】:

执行run方法

休眠被打断

java程序中,只要前台有一个线程在运行,整个java程序进程不会消失,所以此时可以设置一个后台线程,这样即使java进程消失了,此后台线程依然能够继续运行。

/**
 * @author Rollen-Holt 后台线程
 * */
class hello implements Runnable {
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "在运行");
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "线程");
        demo.setDaemon(true);
        demo.start();
    }
}

虽然有一个死循环,但是程序还是可以执行完的。因为在死循环中的线程操作已经设置为后台运行了。

 

/**
 * @author Rollen-Holt 线程的优先级
 * */
class hello implements Runnable {
    public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"运行"+i);
        }
    }
 
    public static void main(String[] args) {
        Thread h1=new Thread(new hello(),"A");
        Thread h2=new Thread(new hello(),"B");
        Thread h3=new Thread(new hello(),"C");
        h1.setPriority(8);
        h2.setPriority(2);
        h3.setPriority(6);
        h1.start();
        h2.start();
        h3.start();
         
    }
}

 

【运行结果】:

A运行0

A运行1

A运行2

A运行3

A运行4

B运行0

C运行0

C运行1

C运行2

C运行3

C运行4

B运行1

B运行2

B运行3

B运行4

但是不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源。所以B运行了1次,C开始运行。运行完轮到B,另外,主线程的优先级是5

 

线程的礼让。

在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。

 

/**
 * @author Rollen-Holt 线程的优先级
 * */
class hello implements Runnable {
    public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"运行"+i);
            if(i==3){
                System.out.println("线程的礼让");
                Thread.currentThread().yield();
            }
        }
    }
 
    public static void main(String[] args) {
        Thread h1=new Thread(new hello(),"A");
        Thread h2=new Thread(new hello(),"B");
        h1.start();
        h2.start();
         
    }
}

 

结果:

A运行0

A运行1

A运行2

A运行3

线程的礼让

A运行4

B运行0

B运行1

B运行2

B运行3

线程的礼让

B运行4

 

同步和死锁:

【问题引出】:比如说对于买票系统,有下面的代码:

class hello implements Runnable {
    public void run() {
        for(int i=0;i<10;++i){
            if(count>0){
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(count--);
            }
        }
    }
 
    public static void main(String[] args) {
        hello he=new hello();
        Thread h1=new Thread(he);
        Thread h2=new Thread(he);
        Thread h3=new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
    private int count=5;
}

【运行结果】:

5

4

3

2

1

0

-1

这里出现了-1,显然这个是错的。,应该票数不能为负值。

如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,

其他的线程必须等到这个线程结束之后才能继续执行。

 

【使用线程同步解决问题】

采用同步的话,可以使用同步代码块和同步方法两种来完成。

【同步代码块】:

语法格式:

synchronized(同步对象){

//需要同步的代码

}

但是一般都把当前对象this作为同步对象。

比如对于上面的买票的问题,如下:

 

class hello implements Runnable {
    public void run() {
        for(int i=0;i<10;++i){
            synchronized (this) {
                if(count>0){
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(count--);
                }
            }
        }
    }
 
    public static void main(String[] args) {
        hello he=new hello();
        Thread h1=new Thread(he);
        Thread h2=new Thread(he);
        Thread h3=new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
    private int count=5;
}

 

【运行结果】:(每一秒输出一个结果)

5

4

3

2

1

【同步方法】

也可以采用同步方法。

语法格式为synchronized 方法返回类型方法名(参数列表){

// 其他代码

}

现在,我们采用同步方法解决上面的问题。

class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 10; ++i) {
            sale();
        }
    }
 
    public synchronized void sale() {
        if (count > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(count--);
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread h1 = new Thread(he);
        Thread h2 = new Thread(he);
        Thread h3 = new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
 
    private int count = 5;
}

【运行结果】(每秒输出一个)

5

4

3

2

1

提醒一下,当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

此处列举经典的生产者和消费者问题。

【生产者和消费者问题】

先看一段有问题的代码。

class Info {
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    private String name = "Rollen";
    private int age = 20;
}
 
/**
 * 生产者
 * */
class Producer implements Runnable{
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }
     
    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                this.info.setName("Rollen");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(20);
                flag=false;
            }else{
                this.info.setName("chunGe");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(100);
                flag=true;
            }
        }
    }
}
/**
 * 消费者类
 * */
class Consumer implements Runnable{
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }
     
    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.info.getName()+"<---->"+this.info.getAge());
        }
    }
}
 
/**
 * 测试类
 * */
class hello{
    public static void main(String[] args) {
        Info info=new Info();
        Producer pro=new Producer(info);
        Consumer con=new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

【运行结果】:

Rollen<---->100

chunGe<---->20

chunGe<---->100

Rollen<---->100

chunGe<---->20

Rollen<---->100

Rollen<---->100

Rollen<---->100

chunGe<---->20

chunGe<---->20

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

大家可以从结果中看到,名字和年龄并没有对于。

 

那么如何解决呢?

1)加入同步

2)加入等待和唤醒

先来看看加入同步会是如何。

class Info {
     
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public synchronized void set(String name, int age){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
    }
     
    public synchronized void get(){
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
    }
    private String name = "Rollen";
    private int age = 20;
}
 
/**
 * 生产者
 * */
class Producer implements Runnable {
    private Info info = null;
 
    Producer(Info info) {
        this.info = info;
    }
 
    public void run() {
        boolean flag = false;
        for (int i = 0; i < 25; ++i) {
            if (flag) {
                 
                this.info.set("Rollen", 20);
                flag = false;
            } else {
                this.info.set("ChunGe", 100);
                flag = true;
            }
        }
    }
}
 
/**
 * 消费者类
 * */
class Consumer implements Runnable {
    private Info info = null;
 
    public Consumer(Info info) {
        this.info = info;
    }
 
    public void run() {
        for (int i = 0; i < 25; ++i) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}
 
/**
 * 测试类
 * */
class hello {
    public static void main(String[] args) {
        Info info = new Info();
        Producer pro = new Producer(info);
        Consumer con = new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

【运行结果】:

Rollen<===>20

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

Rollen<===>20

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

从运行结果来看,错乱的问题解决了,现在是Rollen 对应20,ChunGe对于100

,但是还是出现了重复读取的问题,也肯定有重复覆盖的问题。如果想解决这个问题,就需要使用Object类帮忙了、

,我们可以使用其中的等待和唤醒操作。

要完成上面的功能,我们只需要修改Info类饥渴,在其中加上标志位,并且通过判断标志位完成等待和唤醒的操作,代码如下:

class Info {
     
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public synchronized void set(String name, int age){
        if(!flag){
            try{
                super.wait();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
        flag=false;
        super.notify();
    }
     
    public synchronized void get(){
        if(flag){
            try{
                super.wait();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
         
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
        flag=true;
        super.notify();
    }
    private String name = "Rollen";
    private int age = 20;
    private boolean flag=false;
}
 
/**
 * 生产者
 * */
class Producer implements Runnable {
    private Info info = null;
 
    Producer(Info info) {
        this.info = info;
    }
 
    public void run() {
        boolean flag = false;
        for (int i = 0; i < 25; ++i) {
            if (flag) {
                 
                this.info.set("Rollen", 20);
                flag = false;
            } else {
                this.info.set("ChunGe", 100);
                flag = true;
            }
        }
    }
}
 
/**
 * 消费者类
 * */
class Consumer implements Runnable {
    private Info info = null;
 
    public Consumer(Info info) {
        this.info = info;
    }
 
    public void run() {
        for (int i = 0; i < 25; ++i) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}
 
/**
 * 测试类
 * */
class hello {
    public static void main(String[] args) {
        Info info = new Info();
        Producer pro = new Producer(info);
        Consumer con = new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}
【程序运行结果】:
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
现在看结果就可以知道,之前的问题完全解决。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics