實現多線程的兩種方式--Runnable和Thread/線程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyRunnable implements Runnable{
protected int countDown=10;
private static int taskCount=0;
private final int id = taskCount++;
public MyRunnable(){}
public MyRunnable(int countDown){
this.countDown=countDown;
}
public void run(){
while(countDown-->0){
System.out.println("#"+id+" ("+(countDown>0?countDown:"liftoff!")+")");
//告訴其他線程,讓同級其他線程執行
Thread.yield();
}
}
public static void main(String[]args){
ExecutorService exe = Executors.newCachedThreadPool();
for(int i=0;i<5;i++){
//註釋處也可以正常運行
// Thread t = new Thread(new MyRunnable());
// t.start();
exe.execute(new MyRunnable());
}
//如果不結束,將會一直運行,雖然沒有線程了。
exe.shutdown();
}
}
實現多線程主要有兩種方式,一種是實現Runnable接口,一種是直接繼承Thread類,兩種方式大同小異。實現多線程也可以實現匿名類的方式來實現。上面的例子使用了Runnable接口來實現一個多線程的併發,啓動線程有兩種那個方式,一種是Tread.start(),一種是使用線程池類似,來調用執行。使用線程池可以更好的管理線程,常見的線程池類型有CacheThreadPool,FixThreadPool,和SingleThreadPool.下面我們來看看FixedThreadPool和SingleThreadPool的執行的例子,我們分別寫一個main函數,來執行上面的過程,如下所示:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPool {
public static void main(String[] args){
//FixedThreadPool,設置線程池容量爲5
ExecutorService exe = Executors.newFixedThreadPool(5);
for(int i=0;i<10;i++){
exe.execute(new MyRunnable());
}
}
}
上述沒有使用exe.shutdown()方法,我們來看看結果,運行結果如下:
可以看到,每次同時運行的線程只有5個,一開始運行的線程只有0,1,2,3,4,,o退出之後,5開始運行,1退出之後,6開始運行,這就是FixedThreadPool。同時你應該注意到,程序沒有結束,但是也不再打印新的東西了。
下面我們來看看SingleThreadPool的用法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService exe = Executors.newSingleThreadExecutor();
for(int i=0;i<5;i++){
exe.execute(new MyRunnable());
}
exe.shutdown();
}
}
運行結果如下
可以看到無路如何,這些線程都是並行運行的。
有的時候你需要從你的線程返回你需要的值,你如果用Thread來實現,那是比較困難的。但是你可以用下面這種辦法啊。
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TaskWithResult implements Callable<Integer>{
int begin;
int end;
public TaskWithResult(){
}
public TaskWithResult(int begin,int end){
this.begin=begin;
this.end=end;
}
@Override
public Integer call() throws Exception {
int total=0;
for(int i=begin;i<end;i++){
total+=i;
}
return total;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService exe = Executors.newCachedThreadPool();
ArrayList<Future<Integer>> futureList = new ArrayList<Future<Integer>>();
for(int i=0;i<100000;i+=10000){
futureList.add(exe.submit(new TaskWithResult(i,i+10000)));
}
exe.shutdown();
int total=0;
for(Future f:futureList){
try {
Integer i = (Integer) f.get();
total+=i;
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(total);
int total2=0;
for(int i=0;i<100000;i++){
total2+=i;
}
System.out.println(total2);
}
}
具體的不多說,主要的思路就是實現了從0加到十萬。第一種辦法是使用了多線程的做法,分了是個線程,第二種做法是採用了直接加的做法。當然,也不一定誰好誰壞,這個要看虛擬機的支持了。休眠/讓步/優先級/後臺線程和ThreadFactory
--sleep,以及TimeUnit類,可以自己參考jdk文檔;TimeUnit.[interval].sleep([length】)
import java.util.concurrent.*;
public class MyThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
//將所有的線程都設置爲後臺線程
t.setDaemon(true);
return t;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService exe = Executors.newCachedThreadPool(new MyThreadFactory());
for(int i=0;i<5;i++){
exe.execute(new Mydaemon());
}
exe.shutdown();
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Mydaemon implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<5;i++){
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread()+" "+this);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
加入一個線程,join的應用
一個線程可以在其他線程之上調用join()方法,其效果是等待一段時間直到第二個線程結束才繼續執行。如果某個線程在另一個線程t上調用t.join(),此線程將被掛起,直到目標線程結束才恢復。也可以加上一個超時參數,這樣如果目標線程在這段時間內到期時還沒有結束的話,join方法總是能反回,防止長時間等待。如下所示:
public class Sleeper extends Thread{
public int duration;
public Sleeper(String name, int sleepTime){
super(name);
this.duration = sleepTime;
start();
}
public void run(){
try{
sleep(duration);
}catch(InterruptedException e){
// e.printStackTrace();
System.out.println(getName()+" is interrupt");
return;
}
System.out.println(getName()+" has awakened");
}
public static void main(String[] args){
Sleeper
sleeper1 = new Sleeper("sleeper1", 1500),
sleeper2 = new Sleeper("sleeper2", 1500);
Joiner
Joiner1 = new Joiner("Joiner1", sleeper1),
Joiner2 = new Joiner("joiner2", sleeper2);
sleeper2.interrupt();
}
}
class Joiner extends Thread{
private Sleeper sleeper;
public Joiner(String name,Sleeper sleeper){
super(name);
this.sleeper=sleeper;
start();
}
public void run(){
try{
sleeper.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName()+" join completed");
}
}