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

30 篇文章 0 订阅

内容转载: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 卖票:ticket10
Thread-1 卖票:ticket10
Thread-2 卖票:ticket10
Thread-1 卖票:ticket9
Thread-0 卖票:ticket9
Thread-1 卖票:ticket8
Thread-2 卖票:ticket9
Thread-1 卖票:ticket7
Thread-0 卖票:ticket8
Thread-1 卖票:ticket6
Thread-2 卖票:ticket8
Thread-1 卖票:ticket5
Thread-0 卖票:ticket7
Thread-1 卖票:ticket4
Thread-2 卖票:ticket7
Thread-1 卖票:ticket3
Thread-0 卖票:ticket6
Thread-1 卖票:ticket2
Thread-2 卖票:ticket6
Thread-2 卖票:ticket5
Thread-2 卖票:ticket4
Thread-1 卖票:ticket1
Thread-0 卖票:ticket5
Thread-2 卖票:ticket3
Thread-0 卖票:ticket4
Thread-2 卖票:ticket2
Thread-0 卖票:ticket3
Thread-2 卖票:ticket1
Thread-0 卖票:ticket2
Thread-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 卖票:ticket10
Thread-0 卖票:ticket9
Thread-1 卖票:ticket8
Thread-0 卖票:ticket7
Thread-2 卖票:ticket6
Thread-1 卖票:ticket5
Thread-2 卖票:ticket4
Thread-0 卖票:ticket3
Thread-1 卖票:ticket2
Thread-2 卖票:ticket1
Thread-0 卖票:ticket0
Thread-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 卖票:ticket20
Thread-2 卖票:ticket19
Thread-2 卖票:ticket18
Thread-2 卖票:ticket17
Thread-2 卖票:ticket16
Thread-2 卖票:ticket15
Thread-2 卖票:ticket14
Thread-2 卖票:ticket13
Thread-2 卖票:ticket12
Thread-2 卖票:ticket11
Thread-1 卖票:ticket10
Thread-1 卖票:ticket9
Thread-1 卖票:ticket8
Thread-1 卖票:ticket7
Thread-1 卖票:ticket6
Thread-1 卖票:ticket5
Thread-1 卖票:ticket4
Thread-1 卖票:ticket3
Thread-1 卖票:ticket2
Thread-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 卖票:ticket20
Thread-2 卖票:ticket19
Thread-2 卖票:ticket18
Thread-2 卖票:ticket17
Thread-2 卖票:ticket16
Thread-0 卖票:ticket15
Thread-0 卖票:ticket14
Thread-0 卖票:ticket13
Thread-0 卖票:ticket12
Thread-0 卖票:ticket11
Thread-0 卖票:ticket10
Thread-1 卖票:ticket9
Thread-0 卖票:ticket8
Thread-0 卖票:ticket7
Thread-0 卖票:ticket6
Thread-0 卖票:ticket5
Thread-2 卖票:ticket4
Thread-2 卖票:ticket3
Thread-2 卖票:ticket2
Thread-2 卖票:ticket1

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值