JAVA并发编程1_多线程的实现方式

JAVA中创建线程的两种方式:继承Thread或实现Runnable接口。

1 继承Thread类,重写run方法:

/**
 * 实现线程的第一种方式 :继承Thread
 * 实现数据共享需要设置属性为静态
 * @author qhyuan1992
 *
 */
class MyThread extends Thread{
	private int count;// static
	public MyThread(String id){
		super(id);
	}
	public void run() {
		while (count < 5) {
			count ++;
			System.out.println(currentThread() + "--->" + count);
		}
	}
}
class Test{
	public static void main(String[] args) {
		Thread t1 = new MyThread("thread_1");
		Thread t2 = new MyThread("thread_2");
		t1.start();
		t2.start();
	}
}
// output(未共享资源)
//Thread[thread_1,5,main]--->1
//Thread[thread_2,5,main]--->1
//Thread[thread_1,5,main]--->2
//Thread[thread_1,5,main]--->3
//Thread[thread_1,5,main]--->4
//Thread[thread_2,5,main]--->2
//Thread[thread_1,5,main]--->5
//Thread[thread_2,5,main]--->3
//Thread[thread_2,5,main]--->4
//Thread[thread_2,5,main]--->5

2 实现Runnable接口,重写run方法,作为参数传给Thread对象。

/**
 * 实现线程的第二种方式 :实现Runnable接口
 * 实现数据共享
 * @author qhyuan1992
 */
class MyRunnable implements Runnable{
	private int count;
	public void run() {
		while (count < 5) {
			count ++;
			System.out.println(Thread.currentThread() + "--->" + count);
		}
	}
	public static void main(String[] args) {
		MyRunnable runnable = new MyRunnable();
		Thread t1 = new Thread(runnable);
		Thread t2 = new Thread(runnable);
		t1.start();
		t2.start();
	}
}
// output(使用同一个Runnable对象构造Thread对象实现资源共享)
//Thread[Thread-0,5,main]--->2
//Thread[Thread-1,5,main]--->2
//Thread[Thread-1,5,main]--->3
//Thread[Thread-1,5,main]--->4
//Thread[Thread-1,5,main]--->5

两种方式有何区别?查看源代码可以看到,Thread类是实现了Runnable接口的。

public
class Thread implements Runnable {
    …
    /* What will be run. */
    private Runnable target; // target就是我们使用第二种方法的时候传递的runnable对象

    /* The group of this thread */
private ThreadGroup group;
…
public Thread(Runnable target) {//第二种使用线程的方式的构造函数
    init(null, target, "Thread-" + nextThreadNum(), 0);
 }
}

当调用Thread类的start()方法时,会调用本地方法start0();

public synchronized void start() {
……
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            ……
        }
    }
private native void start0();

总之,会辗转调用到run方法:

 /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
}

如果使用Runnable来构造Thread的话,将调用Runnable对象的run方法;否则,什么也不做。但通过继承的方式来实现线程,由于多态根本没有执行Thread类中的run方法,就会执行重写的run方法。

例如:要是继承自Thread实现了run方法,也通过了Runnable来构造Thread结果会执行哪个run方法呢?答案是确定的:会执行我们写在继承自Thread类中的run方法。

class MyRunnable implements Runnable{
public void run() {
	System.out.println("MyRunnable");
	}
}

class MyThread extends Thread{
	public MyThread(Runnable r){
		super(r);
	}
	public void run() {
		System.out.println("MyThread");
	}
}

class Test{
	public static void main(String[] args) {
		Thread t = new MyThread(new MyRunnable());
		t.start();
	}
}
// output:
//MyThread

到底使用Thread还是Runnable?

实现Runnable接口比继承Thread类所具有的优势:

1.适合多个相同的程序代码的线程去处理同一个资源,继承Thread的需要将共享的资源设置为static。

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

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

将第一个代码的count字段改为static

class MyThread extends Thread{
	private static int count;// static
	public MyThread(String id){
		super(id);
	}
	public void run() {
		while (count < 5) {
			count ++;
			System.out.println(currentThread() + "--->" + count);
		}
	}
}
class Test{
	public static void main(String[] args) {
		Thread t1 = new MyThread("thread_1");
		Thread t2 = new MyThread("thread_2");
		t1.start();
		t2.start();
	}
}

// output (结果不确定)
//Thread[thread_1,5,main]--->2
//Thread[thread_2,5,main]--->2
//Thread[thread_2,5,main]--->3
//Thread[thread_2,5,main]--->4
//Thread[thread_2,5,main]--->5

可以看到使用static的方式也可以共享资源,和使用第二种方式实现多线程一样,前提是创建Thread的Runnable对象是同一个。

细心一点可以看到打印的结构不是我们所预期的,例如:

//Thread[thread_1,5,main]--->2

//Thread[thread_2,5,main]--->2

出现这样的结果就是因为多线程并发的不可控性,关于线程同步这个问题会在后面继续探讨。

时间: 2024-10-19 04:55:55

JAVA并发编程1_多线程的实现方式的相关文章

Java并发编程之多线程同步

线程安全就是防止某个对象或者值在多个线程中被修改而导致的数据不一致问题,因此我们就需要通过同步机制保证在同一时刻只有一个线程能够访问到该对象或数据,修改数据完毕之后,再将最新数据同步到主存中,使得其他线程都能够得到这个最新数据.下面我们就来了解Java一些基本的同步机制. Java提供了一种稍弱的同步机制即volatile变量,用来确保将变量的更新操作通知到其他线程.当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的.然而,在访问volatile变量时不会执行加锁操作

Java并发编程、多线程、线程池…

Java多线程干货系列(1):Java多线程基础http://www.importnew.com/21136.html#comment-651146 40个Java多线程问题总结http://www.importnew.com/18459.html#comment-651217 Java线程面试题 Top 50http://www.importnew.com/12773.html Java并发编程:Thread类的使用http://www.cnblogs.com/dolphin0520/p/39

JAVA并发编程2_线程安全&amp;内存模型

"你永远都不知道一个线程何时在运行!" 在上一篇博客JAVA并发编程1_多线程的实现方式中后面看到多线程中程序运行结果往往不确定,和我们预期结果不一致.这就是线程的不安全.线程的安全性是非常复杂的,没有任何同步的情况下,多线程的执行顺序是不可预测的.当多个线程访问同一个资源时就会出现线程安全问题.例如有一个银行账户,一个线程往里面打钱,一个线程取钱,要是得到不确定的结果那是多么可怕的事情. 引入: 例如下面的程序,在单线程下,执行两次i++理论上i的最终值是12,但是在多线程环境下则不

【Java并发编程】并发编程大合集-值得收藏

http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用,将Java并发编程系列内容系列内容按照由浅入深的学习顺序总结如下,点击相应的标题即可跳转到对应的文章    [Java并发编程]实现多线程的两种方法    [Java并发编程]线程的中断    [Java并发编程]正确挂起.恢复.终止线程    [Java并发编程]守护线程和线程阻塞    [Ja

【Java并发编程】并发编程大合集

转载自:http://blog.csdn.net/ns_code/article/details/17539599 为了方便各位网友学习以及方便自己复习之用,将Java并发编程系列内容系列内容按照由浅入深的学习顺序总结如下,点击相应的标题即可跳转到对应的文章    [Java并发编程]实现多线程的两种方法    [Java并发编程]线程的中断    [Java并发编程]正确挂起.恢复.终止线程    [Java并发编程]守护线程和线程阻塞    [Java并发编程]Volatile关键字(上)

Java并发编程-非阻塞同步方式原子类(Atomic)的使用

非阻塞同步 在大多数情况下,我们为了实现线程安全都会使用Synchronized或lock来加锁进行线程的互斥同步,但互斥同步的最主要的问题就是进行线程的阻塞和唤醒所带来的性能问题,因此这种阻塞也称作阻塞同步.从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都会进行加锁.用户态核心态转换.维护锁的计数器和检查是否有被阻塞的线程需要被唤醒等操作. 随着硬件指令集的发展,我们有了另一个选择:基于冲突检测的乐

19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对

【多线程】Java并发编程:Lock(转载)

原文链接:http://www.cnblogs.com/dolphin0520/p/3923167.html Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题将在下面进行阐述

Java并发编程(8):多线程环境中安全使用集合API(含代码)

Java并发编程(8):多线程环境中安全使用集合API(含代码)JAVA大数据中高级架构 2018-11-09 14:44:47在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对象锁便是一种浪费,另外在不必要的时候如果滥用同步化,也有可能会带来死锁.因此,对于更改集合内容的方法,没有一个是同步化的.集合本质上是非多线程安全的,当多个线程与集合交互时