多线程基础知识第一篇:创建线程3种方式

原创 2015年07月08日 17:30:13

第一,首先说一下为什么要使用多线程。

线程是大多数操作系统调度的基本单元,一个程序作为一个进程来执行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。也就是说单线程程序只能使用一个处理器核心,那么加入再多的处理器核心也无法显著提升程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。

第二,线程的创建。

目前有3种创建线程的方式:

1.继承Thread类的方式:

class SubThread extends Thread {
	private int count;

	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("current thread: " + Thread.currentThread().getName());
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Thread thread1 = new SubThread();
		thread1.setName("线程1");
		thread1.start();
	}
}
       以上代码,用Thread子类的构造器产生一个Thread子类对象并向上转型为Thread类型。之后可以给线程对象设置名字、设置优先级,然后调用start()方法启动线程。如果不给线程设置名字,则线程的名字默认为Thread-0、Thread-1这种格式的。如果不给线程设置优先级,则线程的优先级默认是5,即是Thread.NORM_PRIORITY。

       如果想在创建线程的时候就设置名字,则可创建一个String类型的成员变量,然后调用以此变量为参数的构造器就可得到一个线程对象,值得注意的是,必须要在此构造器中调用父类也就是Thread的参数是String类型的构造器:

class SubThread extends Thread {
	private int count;
	private String name;

	public SubThread(String name) {
		super(name);
		this.name = name;
	}

	public SubThread() {
		super();
	}

	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("current thread: " + Thread.currentThread().getName());
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Thread thread1 = new SubThread("线程1");
		thread1.start();
	}
}
       之前一直以为,继承Thread类创建线程的方式,多个线程不能共享Thread子类的成员变量。其实这是不对的。如果这个成员变量是一般类型(基本类型、String类型、Date类型、集合类型),这种说法是成立的,毕竟每一次调用new SubThread()方法就创建了一个新的对象,这些个成员变量都是不同对象的成员变量。但是如果这个成员变量是自定义的类类型,而且线程对象是调用以此自定义类类型变量为参数的构造器生成的,则此时多个线程可以共享Thread子类的成员变量,毕竟调用有参构造器时传入的参数是同一个:

class Account {
	private int count = 500;

	public void dec() {
		while (count > 1) {
			synchronized (this) {
				count = count - 1;
				System.out.println("current thread: " + Thread.currentThread().getName() + ",count=" + count);
			}
		}
	}
}

class SubThread extends Thread {
	private String name;
	private Account account;

	public SubThread(String name, Account account) {
		super(name);
		this.name = name;
		this.account = account;
	}

	public SubThread() {
		super();
	}

	public void run() {
		account.dec();
	}
}

public class ThreadTest {
	public static void main(String[] args) {
               Account account = new Account();
		Thread thread1 = new SubThread("线程1", account);
		Thread thread2 = new SubThread("线程2", account);
		thread1.start();
		thread2.start();
	}
}
       如上,调用第三方类变量作为参数的构造器来创建线程,当传入同一个第三方类实例时,创建的多个线程就可以共享这个第三方类实例,当然也就共享这个第三方类实例的成员变量了。   

2.实现Runnable接口的方式:

class ThreadImpl implements Runnable {
	private int count;

	@Override
	public void run() {
		while (true) {
			count = count + 1;
			System.out.println("count="+count);
			if (count > 100) {
				break;
			}
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Runnable target = new ThreadImpl();
		Thread thread1 = new Thread(target);
		thread1.start();
	}
}

       以上,其实是用了Thread(Runnable target) 构造器,传入一个Runnable对象就可以得到一个线程对象。如果想在创建线程的时候给线程设置名字,则可以用另一个构造器Thread(Runnable target, String name) ,第二个参数传线程名字就好了。其实Thread类还有一个public的无参构造器,但是用此无参构造器生成的线程对象调用start()启动后执行的run()方法是Thread类的run()方法,什么也不做,所以此无参构造器实际上没有啥作用。

       如果创建N个线程传的是同一个Runnable对象,则这N个线程共享此Runnable对象的成员变量。

class ThreadImpl implements Runnable {
	private int count;

	@Override
	public void run() {
		while (true) {
			count = count + 1;
			System.out.println("当前线程:" + Thread.currentThread().getName() + ",count:" + count);
			if (count > 100) {
			        break;
			}
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Runnable target = new ThreadImpl();
		Thread thread1 = new Thread(target, "线程1");
		Thread thread2 = new Thread(target, "线程2");
		thread1.start();
		thread2.start();
	}
}
       以上,用两个线程对同一个变量account进行增长操作,在打印结果中线程1和线程2交叉出现,顺序不一定,account的值从1一直升到101,中间顺序可能紊乱,也能有几条相同,这就是线程不安全。

3.实现Callable接口的方式:

class CallableImpl implements Callable<Integer> {
	private int count;

	@Override
	public Integer call() {
		while (true) {
			count = count + 1;
			System.out.println("当前线程:" + Thread.currentThread().getName()
					+ ",count=" + count);
			if (count > 100) {
				break;
			}
		}
		return count;
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		CallableImpl callableImpl = new CallableImpl();
		FutureTask<Integer> futureTask1 = new FutureTask<Integer>(callableImpl);
		new Thread(futureTask1, "线程1").start();
		try {
			System.out.println("线程1的返回值: " + futureTask1.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

      以上,创建了Callable接口的实现类,并重写了Callable的call方法。创建该Callable实现类的对象,并传入到FutureTask的构造器FutureTask(Callable<V> callable)中,得到FutureTask对象。因为FutureTask类实现了Runnable接口,所以该FutureTask对象可以传到Thread的构造器Thread(Runnable target)中,从而得到一个线程对象。同Runnable实现类的run方法体是线程的执行体一样,Callable实现类的call方法体即为线程的执行体。不同的是,call方法是有返回值的,调用FutureTask对象的重载的get()方法可以得到线程的返回值,而至于返回值用不用,则看具体业务。

      以这种方式创建线程,如果想多个线程共享一个成员变量,只需创建一个Callable对象,然后用该Callable对象创建多个FutureTask对象,再创建多个线程即可,这样多个线程就可以共同操作那一个Callable对象的成员变量了。见以下代码:

class CallableImpl implements Callable<Object> {
	private int count;

	@Override
	public Object call() {
		while (true) {
			count = count + 1;
			System.out.println("当前线程:" + Thread.currentThread().getName()
					+ ",count=" + count);
			if (count > 100) {
				break;
			}
		}
		return null;
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		CallableImpl callableImpl = new CallableImpl();
		FutureTask<Object> futureTask1 = new FutureTask<Object>(callableImpl);
		FutureTask<Object> futureTask2 = new FutureTask<Object>(callableImpl);
		new Thread(futureTask1, "线程1").start();
		new Thread(futureTask2, "线程2").start();
	}
}
注意,以上代码也存在线程安全问题。

第三,线程的优先级:

线程的优先级是用1到10的整数表示的,数值越大,表示优先级越高,获得时间片从而被CPU执行的机会越大。

Thread类有3个final的静态变量,Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,值分别是1、5、10,分别对应着线程的最低优先级、普通优先级和最大优先级。值得注意的是,每个线程在new出来之后的优先级都是NORM_PRIORITY,调用getPriority()方法可以得到线程的优先级。而如果要提升或者降低优先级,则只需要调用setPriority(int newPriority)方法即可。

linux下C语言多线程(第一篇)创建线程

Linux下用C开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。 #include int pthread_create(pthr...

多线程第一篇:使用_beginthreadex创建线程

先来一个CreateThread函数和_beginthreadex函数详解: HANDLE WINAPI CreateThread ( _In_opt_ LPSECURITY_ATTRIBU...

Java基础:多线程(1)--线程的概述、创建线程的方式、线程的多种状态、线程常用的方法

一、线程的概述进程:正在运行的程序,负责了这个程序的内存空间分配,代表了内存中的执行区域。 线程:就是在一个进程中负责一个执行路径。 多线程:就是在一个进程中多个执行路径同时执行。1.1 多线程的...
  • Yummmy_
  • Yummmy_
  • 2017年03月25日 00:13
  • 371

Java【多线程知识总结(1)】用Thread类创建线程

解析说明:sun公司提供了一个 Thread 类,类体里有run()方法、start()方法,启用start().  方法可以使run()方法运行.  Thread类中的run方法是空的.所以声明...

多线程 创建线程的两种方式

创建线程有两种方式:第一种是继承Thread类,第二种是实现Runnable接口。下面详细介绍两种线程创建的方式,这个内容为必须掌握的。到最后会对比两种方式创建线程的优势和劣势。 1继承Thre...

多线程-01,创建线程的两种方式

基本概念 进程:是一个正在执行中的程序。每一个程序都有一个执行循序,该循序是一个执行路径或者叫一个控制单元 线程:就是进程中的一个独立的控制单元。线程进程的执行 如何在自定义的代码中,自定义一个线程...

【多线程】两种方式创建线程

第一种方式:继承Thread类 public class ThreadDemo extends Thread{ @Override public void run(){ ...

多线程:创建线程的几种方式

1.线程的相关概念 并行:指在同一时刻,有多条指令在多个处理器上同时执行。 并发:指在同一时刻,只能有一条指令执行,但多个进程指令之间被快速轮换执行,使得在宏观上具有多个线程同时执行的效果。 进程:进...

【多线程】创建线程的三种方式

进程与线程的区别进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行。举一个在博客中看到的例子。 进程就好比工厂的车间,它代表CPU所能处...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:多线程基础知识第一篇:创建线程3种方式
举报原因:
原因补充:

(最多只允许输入30个字)