JAVA多线程之——线程的实现方式

来源:互联网 发布:淘宝代充平台 编辑:程序博客网 时间:2024/05/23 18:55

内容转载:http://www.cnblogs.com/skywang12345/p/3479063.html

概要
本章我们学习多线程的两种常用实现方式:继承Thread类与实现Runnable接口。
此外我们还可以通过JUC(java.util.concurrent)中的线程池来实现。本章内容包括:
Thread与Runnable简介
Thread 是一个类,而这个类本身就实现了Runnable接口。它的声明如下:

   public class Thread implements Runnable{}

Runnable 是一个接口。该接口中只包含了一个run()方法。它的声明如下:

public interface Runnable {    public abstract void run();}

通过Runnable实现线程:

    new Thread(new Runnable(){            @Override            public void run() {            }        });

Thread与Runnable的异同
1.Thread与Runnable都能实现多线程。
2.Thread是类,Runnable是接口。JAVA是单继承,所以Runnable具有更好的扩展性。而Thread本身就实现了Runnable接口。Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。
Thread和Runnable的多线程示例
1. Thread的多线程示例

// ThreadTest.java 源码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(this.getName()+" 卖票:ticket"+this.ticket--);            }        }    } };public class ThreadTest {      public static void main(String[] args) {          // 启动3个线程t1,t2,t3;每个线程各卖10张票!        MyThread t1=new MyThread();        MyThread t2=new MyThread();        MyThread t3=new MyThread();        t1.start();        t2.start();        t3.start();    }  }

运行结果:

Thread-0 卖票:ticket10Thread-1 卖票:ticket10Thread-2 卖票:ticket10Thread-1 卖票:ticket9Thread-0 卖票:ticket9Thread-1 卖票:ticket8Thread-2 卖票:ticket9Thread-1 卖票:ticket7Thread-0 卖票:ticket8Thread-1 卖票:ticket6Thread-2 卖票:ticket8Thread-1 卖票:ticket5Thread-0 卖票:ticket7Thread-1 卖票:ticket4Thread-2 卖票:ticket7Thread-1 卖票:ticket3Thread-0 卖票:ticket6Thread-1 卖票:ticket2Thread-2 卖票:ticket6Thread-2 卖票:ticket5Thread-2 卖票:ticket4Thread-1 卖票:ticket1Thread-0 卖票:ticket5Thread-2 卖票:ticket3Thread-0 卖票:ticket4Thread-2 卖票:ticket2Thread-0 卖票:ticket3Thread-2 卖票:ticket1Thread-0 卖票:ticket2Thread-0 卖票:ticket1

通过Thread启动3个线程去卖票,会导致每个线程都卖出了10张票。
2. 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(Thread.currentThread().getName()+" 卖票:ticket"+this.ticket--);            }        }    } }; public class RunnableTest {      public static void main(String[] args) {          MyThread mt=new MyThread();        // 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!        Thread t1=new Thread(mt);        Thread t2=new Thread(mt);        Thread t3=new Thread(mt);        t1.start();        t2.start();        t3.start();    }  }

**这里代码会有几个问题:
1.就是MyThread 是普通类,而main方法为static方法,在静态方法中,必须将MyThread 声明为静态类。
2.上述代码的执行会导致线程资源竞争出问题。因为3个线程都共享一个对象mt,这样所有线程都竞争mt中的ticket的变量时,可能会出现线程票数为负数。因为当线程1获取最后一张票为1时。如果这个线程还没执行完毕卖票,那么另外一个线程2得到的票数也为1.当线程1卖完票,此时线程2已经获取的为1,此时实际票已经为0.那么得到的结果就会为-1.*

public class ThreadTest1 {    public static void main(String[] args) {        MyThread mt = new MyThread();        // 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!        Thread t1 = new Thread(mt);        Thread t2 = new Thread(mt);        Thread t3 = new Thread(mt);        t1.start();        t2.start();        t3.start();    }    // RunnableTest.java 源码    static class MyThread implements Runnable {        private int ticket = 10;        Object obj = new Object();        public void run() {            //synchronized (obj) {                for (int i = 0; i < 10; i++) {                    if (this.ticket > 0) {                        try {                            Thread.currentThread().sleep(500);                        } catch (InterruptedException e) {                            // TODO Auto-generated catch block                            e.printStackTrace();                        }                        System.out.println(Thread.currentThread().getName()                                + " 卖票:ticket" + this.ticket--);                        // }                    }                }            }        //}    };}

如上代码,在每次卖票之前进行线程休眠。得到结果如下:

Thread-2 卖票:ticket10Thread-0 卖票:ticket9Thread-1 卖票:ticket8Thread-0 卖票:ticket7Thread-2 卖票:ticket6Thread-1 卖票:ticket5Thread-2 卖票:ticket4Thread-0 卖票:ticket3Thread-1 卖票:ticket2Thread-2 卖票:ticket1Thread-0 卖票:ticket0Thread-1 卖票:ticket-1

如果将ticket变量声明为volatile类型呢?一个变量声明为volatile的原则之一就是该变量计算的值要不依赖前面的值。此处不合适。后面会学习到这个变量此处不详细说明。那么将run方法同步呢?

public class ThreadTest1 {    public static void main(String[] args) {        MyThread mt = new MyThread();        // 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!        Thread t1 = new Thread(mt);        Thread t2 = new Thread(mt);        Thread t3 = new Thread(mt);        t1.start();        t2.start();        t3.start();    }    // RunnableTest.java 源码    static class MyThread implements Runnable {        private int ticket = 20;        Object obj = new Object();        public synchronized void run() {            //synchronized (obj) {                for (int i = 0; i < 10; i++) {                    if (this.ticket > 0) {                        try {                            Thread.currentThread().sleep(500);                        } catch (InterruptedException e) {                            // TODO Auto-generated catch block                            e.printStackTrace();                        }                        System.out.println(Thread.currentThread().getName()                                + " 卖票:ticket" + this.ticket--);                        // }                    }                }            }        //}    };}

将run方法同步得到的结果为:

Thread-2 卖票:ticket20Thread-2 卖票:ticket19Thread-2 卖票:ticket18Thread-2 卖票:ticket17Thread-2 卖票:ticket16Thread-2 卖票:ticket15Thread-2 卖票:ticket14Thread-2 卖票:ticket13Thread-2 卖票:ticket12Thread-2 卖票:ticket11Thread-1 卖票:ticket10Thread-1 卖票:ticket9Thread-1 卖票:ticket8Thread-1 卖票:ticket7Thread-1 卖票:ticket6Thread-1 卖票:ticket5Thread-1 卖票:ticket4Thread-1 卖票:ticket3Thread-1 卖票:ticket2Thread-1 卖票:ticket1

因为如果将整个run方法同步,那么就是锁住mt这个对象,当第一个线程获取了mt的锁之后,后面的线程由于都是共享mt同一个锁,所以,只能等线程1释放该对象的锁。那么,就会导致线程1卖完10张飘。再释放锁。接下来获取mt对象锁的线程再卖10张。这样就等于要一个一个的卖,而不能并发卖。同理声明一个对象obj,在锁住该对象,跟同步run方法运行的结果一致。那该如何呢?

import java.util.concurrent.atomic.AtomicInteger;public class ThreadTest1 {    public static void main(String[] args) {        MyThread mt = new MyThread();        // 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!        Thread t1 = new Thread(mt);        Thread t2 = new Thread(mt);        Thread t3 = new Thread(mt);        t1.start();        t2.start();        t3.start();    }    // RunnableTest.java 源码    static class MyThread implements Runnable {        private int  ticket = 20;        Object obj = new Object();        public void run() {                for (int i = 0; i < 10; i++) {                    synchronized (obj) {                    if (ticket > 0) {                        try {                            Thread.currentThread().sleep(500);                        } catch (InterruptedException e) {                            // TODO Auto-generated catch block                            e.printStackTrace();                        }                        System.out.println(Thread.currentThread().getName()                                + " 卖票:ticket" + this.ticket--);                         }                    }                }            }        //}    };}

运行结果:

Thread-2 卖票:ticket20Thread-2 卖票:ticket19Thread-2 卖票:ticket18Thread-2 卖票:ticket17Thread-2 卖票:ticket16Thread-0 卖票:ticket15Thread-0 卖票:ticket14Thread-0 卖票:ticket13Thread-0 卖票:ticket12Thread-0 卖票:ticket11Thread-0 卖票:ticket10Thread-1 卖票:ticket9Thread-0 卖票:ticket8Thread-0 卖票:ticket7Thread-0 卖票:ticket6Thread-0 卖票:ticket5Thread-2 卖票:ticket4Thread-2 卖票:ticket3Thread-2 卖票:ticket2Thread-2 卖票:ticket1

通过声明一个变量,然后在每次卖票判断之前进行加锁。则可以解决这问题。

0 0
原创粉丝点击