项目中的多线程并发以及字符串的常用方法

  • Post author:
  • Post category:其他




程序、进程、线程之间的关系


程序:一段静态的代码,静态的对象。


进程:程序的一次执行过程,正在运行的程序。如:运行中的QQ、运行中的MP3就是分别的两个进程。


程序是静态的,进程是动态的。


线程:进程可以细划分为线程。是一个程序内部的一条执行路径。


如果一个程序在同一个时间执行了多个任务,那么就说这个程序是支持多线程的。


如:操作系统下可以运行多个进程,每个进程下可以执行多个线程。



什么时候使用到多线程


1)程序需要在同一时间执行两个和多个任务的时候


2)程序需要在执行的时候有一些等待的任务;如用户的输入、搜索、文件的读写操作、网络的操作等。


3)需要一些后台运行的程序时。




创建线程的三种方式:



public class TraditionalThread {




/**



* 创建线程的三种方式



*/



public static void main(String[] args) {







Thread thread = new Thread(){


//


使用匿名内部类创建线程



public void run() {




while(true){




try {




Thread.sleep(500);



} catch (InterruptedException e) {




e.printStackTrace();



}



System.out.println(“1:” + Thread.currentThread().getName());






}



}



};



thread.start();




//


实现 Runnable



Thread thread2 = new Thread(new Runnable(){




@Override



public void run() {




while(true){




try {




Thread.sleep(500);



} catch (InterruptedException e) {




e.printStackTrace();



}



System.out.println(“1:” + Thread.currentThread().getName());



}








}



});



thread2.start();

/**

* 实现Callable

*/



int taskSize = 5;



// 创建一个线程池



ExecutorService pool = Executors.newFixedThreadPool(taskSize);



// 创建多个有返回值的任务



List<Future> list = new ArrayList<Future>();



Vector vector=new Vector();



List<String> list1=new ArrayList<String>();



list1.add(“from capture where id in(1,2)”);



list1.add(“from capture where id in(3,4)”);



list1.add(“from capture where id in(5,6)”);



list1.add(“from capture where id in(7,8)”);



list1.add(“from capture where id in(9,10)”);



for (String s:list1) {



Callable c = new MyCallable(s, vector);



// 执行任务并获取Future对象



Future f =  pool.submit(c);



list.add(f);



}



// 关闭线程池



pool.shutdown();



// 获取所有并发任务的运行结果



System.out.println(vector);



}







}

class MyCallable implements Callable<Object> {

private String  sql;

private Vector vector;

MyCallable(String  sql,Vector vector) {

this.sql = sql;

this.vector=vector;

}

public Object call() throws Exception {

//       System.out.println(“>>>” + taskNum + “任务启动”);



vector.add(sql);



return vector;

}

}



窗口售票的案例:



1)继承Thread类的方式:

/**
 * 三个窗口共同出售100张票  为了保证ticket100 被三个实例共用 需要声明为static
 * 否则每个线程都会有着自己的实例 也就是说每个实例都有着ticket=100属性。
 * 即:各自有着100张票 这是不合理的。
 */
public class TestWindow {
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();
        window1.setName("窗口1: ");
        window2.setName("窗口2: ");
        window3.setName("窗口3: ");
        window1.start();
        window2.start();
        window3.start();
    }
}
class Window extends Thread {
   static  int ticket = 100;
    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "售票,票号为" +ticket--);
            }else{
                break;
            }
        }
    }
}



2)实现Runnable接口的方式

/**
 * 创建线程的方式二
 * 实现Runnable接口的方式创建线程
 */
public class MyRunnable {
    public static void main(String[] args) {
        Window window = new Window();
        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);
        thread1.setName("窗口1:");
        thread2.setName("窗口2:");
        thread3.setName("窗口3:");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class Window implements Runnable {
    int ticket = 10;

    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);
            } else {
                break;
            }
        }
    }
}



3)实现Callable接口的方式


详细介绍Callable




而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。



Callable接口代表一段可以调用并返回结果的代码;Future接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future用于获取结果。


Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法在线程池中执行Callable内的任务。由于Callable任务是并行的(并行就是整体看上去是并行的,其实在某个时间点只有一个线程在执行),我们必须等待它返回的结果。


java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。


那么怎么使用Callable呢?





ExecutorService





一般情况下是配合

ExecutorService

来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);


第一个submit方法里面的参数类型就是Callable。


暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。


一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。


Future


Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。


Future类位于java.util.concurrent包下,它是一个接口:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}


在Future接口中声明了5个方法,下面依次解释每个方法的作用:


cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。


isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。


isDone方法表示任务是否已经完成,若任务完成,则返回true;


get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;


get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。



也就是说Future提供了三种功能:


1)判断任务是否完成;


2)能够中断任务;


3)能够获取任务执行结果。


因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。


FutureTask


FutureTask实现了RunnableFuture接口,这个接口的定义如下:

public interface RunnableFuture<V> extends Runnable, Future<V> {  
    void run();  
}  


可以看到这个接口实现了Runnable和Future接口,接口中的具体实现由FutureTask来实现。这个类的两个构造方法如下 :

public FutureTask(Callable<V> callable) {  
        if (callable == null)  
            throw new NullPointerException();  
        sync = new Sync(callable);  
    }  
    public FutureTask(Runnable runnable, V result) {  
        sync = new Sync(Executors.callable(runnable, result));  
    }  


如上提供了两个构造函数,一个以Callable为参数,另外一个以Runnable为参数。这些类之间的关联对于任务建模的办法非常灵活,允许你基于FutureTask的Runnable特性(因为它实现了Runnable接口),把任务写成Callable,然后封装进一个由执行者调度并在必要时可以取消的FutureTask。


FutureTask可以由执行者调度,这一点很关键。它对外提供的方法基本上就是Future和Runnable接口的组合:get()、cancel、isDone()、isCancelled()和run(),而run()方法通常都是由执行者调用,我们基本上不需要直接调用它。


一个FutureTask的例子

/**
 * 创建线程的方式三
 * 实现Callable接口方式 然后重写call()方法
 * 1)此方式和Runnable类似 ; 但是Runnable不会返回结果,并且无法抛出返回结果的异常
 * 而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值,
 * 2)下面的应用通过ExecutorService的submit方法执行Callable
 * 3)FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,
 */

public class MyCallAble {
    public static void main(String[] args) {
        int taskSize = 5;
        // 创建一个线程池并返回ExecutorService实例
        ExecutorService executor = Executors.newFixedThreadPool(taskSize);
        SelfCallable callable1 = new SelfCallable(1000);                       // 要执行的任务
        SelfCallable callable2 = new SelfCallable(2000);
        FutureTask<String> futureTask1 = new FutureTask<String>(callable1);// 将Callable写的任务封装到一个由执行者调度的FutureTask对象
        FutureTask<String> futureTask2 = new FutureTask<String>(callable2);
        executor.submit(futureTask1);  // 执行任务 等同于 Future future1= executor.submit(callable1);
        executor.submit(futureTask2);//Future future2= executor.submit(callable2);
        try {
            System.out.println(futureTask1.get());//通过future获取返回值  future1.get()
            System.out.println(futureTask2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class SelfCallable implements Callable<String> {
    private long waitTime;

    public SelfCallable(int times) {
        this.waitTime = times;
    }

    public String call() {
        try {
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Thread.currentThread().getName() + "  Test";
    }
}



总结:



线程中的常用方法


start():启动线程;并执行相应的run方法。


run():子线程要执行的代码存放在run()方法中。


currentThread():获取当前的线程。


getName():获取当前线程的名字。


setName():设置当前线程的name。


yield():调用此方法的线程会释放当前CPU的执行权。



join():方法可以使得一个线程在另一个线程结束后再执行。如果join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程实例完成了执行。



isAlive():判断当前的线程是否存活。


sleep(long l):让当前的线程睡眠l毫秒。


interrupt():打断其睡眠阻塞。

以下的三个方法都是对象的方法


wait():让当前的线程挂起并放弃CPU、同步资源、使别的线程可以访问并修改共享的资源,而当前的线程只能排队等候再次对资源的访问。会释放对象的锁让别的线程使用同步资源。


notify():唤醒正在排队等候的同步资源线程优先级最高者结束等待。


notifyAll():唤醒正在排队等候的同步资源所有线程结束等待。


Object提供的这三个方法只能在Synchronized方法和Synchronized代码块使用,否则会报异常。



比较创建线程三种方式的区别


1)避免了Java单继承的局限性。


2)如果多个线程操作同一份资源(数据)更适合使用实现Runnable接口的方式。


3)Callable优于Thread和Runnable的地方就是 可以获取到返回值。



定时器


public class TraditionalTimerTest {





private static int count = 0;





public static void main(String[] args) {





new Timer().schedule(new TimerTask() {














@Override





public void run() {


System.out.println(“bombing!”);











}





}, 10000);







}}


每隔10s打印bombing



其中Timer为定时器,而TimerTask为任务。


线程的同步互斥(线程安全问题)




当多个线程访问同一个对象的时候就会出现线程安全问题


。(如银行的存款和取款)

使用synchronized关键字 来修饰一个方法或者一个代码块,来保证在同一个时刻最多只有一个线程

来执行代代码。


方式一:



同步代码块

synchronized(同步监视器){
//需要被同步的代码块(即为操作共享数据的代码)
}
1、共享数据:多个线程共同操作的同一数据(变量)

2、同步监视器:由一个类的对象充当,那个线程获取此监视器,谁就执行大括号里被同步的代码。俗称”锁”  Note:所有的线程必须共用一把锁

任何对象都能作为同步监视器,但是只能是类的全局变量,不能是局部变量。



在实现的方式创建线程可以使用this作为同步监视器,使用继承的方式创建线程不能使用this作为同步监视器。



可以使用this 即:当前的对象作为同步监视器。

/**
 * 创建线程的方式二
 * 实现Runnable接口的方式创建线程
 */
public class MyRunnable {
    public static void main(String[] args) {
        Window window = new Window();
        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);
        thread1.setName("窗口1:");
        thread2.setName("窗口2:");
        thread3.setName("窗口3:");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class Window implements Runnable {
    int ticket = 10;
    Object obj = new Object();

    public void run() {
     //Animal animal=new Animal();//局部变量
        while (true) {
            synchronized (obj) {// 使用this替换obj 表示执行此方法的当前对象  不能使用animal局部变量作为同步监视器。
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);
                }
            }
        }
    }
}



方式二:



同步方法


操作共享数据的方法声明为synchronized。即为同步方法,能够保证当其中一个线程执行此方法的时候,其他线程在此方法外进行等待直至此线程执行完毕。

同步方法也有锁机制:同步方法的锁就是this。

/**
 * 创建线程的方式二
 * 实现Runnable接口的方式创建线程
 */
public class MyRunnable {
    public static void main(String[] args) {
        Window window = new Window();
        Thread thread1 = new Thread(window);
        Thread thread2 = new Thread(window);
        Thread thread3 = new Thread(window);
        thread1.setName("窗口1:");
        thread2.setName("窗口2:");
        thread3.setName("窗口3:");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class Window implements Runnable {
    int ticket = 10;
    Object obj = new Object();
    public void run() {
        while (true) {
//            synchronized (obj) {// 使用this替换obj 表示执行此方法的当前对象
//                if (ticket > 0) {
//                    System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);
//                }
//            }
            show();//同步方法
        }
    }
    public  synchronized void show(){
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);
        }
    }
}



案例:

两个储户分别向这个账户中存3000 一次存储1000 存3次 每次存储完打余额。

/**
 * 银行有一个账户
 * 两个储户分别向这个账户中存3000 一次存储1000 存3次 每次存储完打余额。
 * 1、是否需要多线程  两个储户
 * 2、是否有共享数据  同一个账户
 * 3、是否考虑线程的同步
 */
public class TestAccount {
    public static void main(String[] args) {
        Account account = new Account();
        Customer customer1 = new Customer(account);
        Customer customer2 = new Customer(account);
        Thread t1=new Thread(customer1);
        Thread t2=new Thread(customer2);
        t1.setName("储户1:");
        t2.setName("储户2:");
        t1.start();
        t2.start();
    }
}

class Customer implements Runnable {
    Account account;

    public Customer(Account account) {
        this.account = account;
    }

    public void run() {
        for (int i = 0; i < 3; i++) {
            account.deposit(1000);
        }
    }
}
class Account {
    double balance;//余额

    public Account() {

    }
    public synchronized  void deposit(double amt) {
        this.balance += amt;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + balance);
    }



线程的中断






Thread.interrupt():中断睡眠阻塞。

public static void main(String[] args) {






/**



* 林永健,处于睡眠阻塞的线程



*/



final Thread lin=new Thread(new Runnable(){




public void run(){




System.out.println(“林:刚美完容,睡觉去了!”);



try{




Thread.sleep(1000000);//此线程进入睡眠阻塞



}catch(InterruptedException  e){




System.out.println(“林:干嘛呢,干嘛呢!都破相了!”);






}



}



});






/**



* 黄宏线程,用于中断林永健睡眠阻塞



*/



Thread huanghong=new Thread(new Runnable(){




public void run(){




System.out.println(“开始砸墙!”);



for(int i=0;i<5;i++){




System.out.println(“黄:80!”);



try{Thread.sleep(1000);



}catch(InterruptedException e){






}



}



System.out.println(“咣当!”);



System.out.println(“黄:搞定!”);







/**



* 一个方法的局部内部类中若要引用该方法的其他局部变量



* 那么这个变量必须是final的



*




*/



lin.interrupt();//中断林永健线程,打断其睡眠阻塞



}



});



lin.start();



huanghong.start();

}

}



线程的死锁


1)死锁的问题:处理线程同步时容易出现。

2)不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

3)写代码时,要避免死锁!

public class TestDeadLock {
    static StringBuffer sb1 = new StringBuffer();
    static StringBuffer sb2 = new StringBuffer();
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                synchronized (sb1) {
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    sb1.append("A");
                    synchronized (sb2) {
                        sb2.append("B");
                        System.out.println(sb1);
                        System.out.println(sb2);
                    }
                }
            }
        }.start();

        new Thread() {
            public void run() {
                synchronized (sb2) {
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    sb1.append("C");
                    synchronized (sb1) {
                        sb2.append("D");
                        System.out.println(sb1);
                        System.out.println(sb2);
                    }
                }
            }
        }.start();
    }
}



线程通信


notify():唤醒正在排队等候的同步资源线程优先级最高者结束等待。


notifyAll():唤醒正在排队等候的同步资源所有线程结束等待。


Object提供的这三个方法只能在Synchronized方法和Synchronized代码块使用,否则会报异常。



案例:使用两个线程打印1-100 线程1、线程2交替打印。

//线程通信。如下的三个关键字使用的话,都得在同步代码块或同步方法中。
//wait():一旦一个线程执行到wait(),就释放当前的锁。
//notify()/notifyAll():唤醒wait的一个或所有的线程
//使用两个线程打印 1-100. 线程1, 线程2 交替打印
class PrintNum implements Runnable {
    int num = 1;
    Object obj = new Object();
    public void run() {
        while (true) {
            synchronized (obj) {
                obj.notify();//第二个线程握住锁后  唤醒第一个线程 
                if (num <= 100) {
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":"
                            + num);
                    num++;
                } else {
                    break;
                }
                try {
                    obj.wait();//释放锁 让另一个线程执行
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}
public class TestCommunication {
    public static void main(String[] args) {
        PrintNum p = new PrintNum();
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);

        t1.setName("甲");
        t2.setName("乙");

        t1.start();
        t2.start();
    }



线程的生命周期


现成生命周期的5中状态

新建状态 :Thread thread=new Thread();

就绪状态 :线程已经被启动(start),但是等着cpu分配时间片段。

运行状态: 线程获得了cpu的资源。

堵塞状态: 由于某种原因导致正在运行的线程让出了cpu并暂停了自己的执行。

死亡状态 :线程执行完毕或者被其他的线程杀死。


守护线程




java的垃圾回收就是一个守护线程



某个线程对象调用setDaemon(true)那么这个线程就是守护线程。守护线程必须在线程启动的时候执行。

/**



* 后台线程与前台线程

* @author Administrator

*

*/

public class ThreadDemo3 {


public static void main(String[] args) {





Thread rose=new Thread(new Runnable(){





public void run(){





for(int i=0;i<10;i++){





System.out.println(“rose:Let me go !”);





try{ Thread.sleep(1000);





}catch(InterruptedException e){





}





}





System.out.println(“rose:AAAAAaaaaa….”);





System.out.println(“噗通!”);





}





});





Thread jack=new Thread(new Runnable(){





public void run(){





while(true){





System.out.println(“jack:you jump! I jump!”);





try{





Thread.sleep(1000);






}catch(InterruptedException e){







}





}





}





});





/**





* 设置后台线程的方法必须在该线程启动前调用





*/





jack.setDaemon(true);





jack.start();





rose.start();





System.out.println(“main方法执行结束了”);





}

}



线程中的sleep和notify方法








/**

* Wait()  notify()方法

* @author Administrator

*

*/

public class WaiAndNotifyDemo {







public static boolean finish=false;






public static void main(String[] args) {





/**





* 两个线程并发运行





* 一个线程用于下载图片





* 另一个线程用于显示图片





* 这里就出现了一个问题,显示图片的线程应当等待下载图片的





* 线程将图片下载后再进行显示。





*/






String ids=”1,2,3,4,5,6,7,8,9,10″;





String[] split=ids.split(“,”);





List list=new ArrayList();





for(String str:split){







list.add(str);





}





List<String> result = new ArrayList<String>();





String temp=null;





for (int i = 0; i < list.size(); i++) {





if(i%2==0){





temp=list.get(i)+”,”;





}





if (i%2!=0) {





temp+=list.get(i);





result.add(temp);





temp=null;





}





}





for(String t:result){





System.out.println(t);





}





//下载图片的线程





final Thread downLoadThread =new Thread(new Runnable(){





public void run(){





System.out.println(“开始下载图片。。。”);





try{





Thread.sleep(5000);






}catch(InterruptedException e){





e.printStackTrace();





}





System.out.println(“下载图片完毕”);





finish=true;





/**





* 通知在当前对象上等待的线程回到runnable状态





* 这里的this就是downLoadThread





* 而下面的displayThread 就是在当前对象上等待的





* 所以调用this.notify()方法会调用displayThread





* 解除等待阻塞,使其可以继续进行





*/





synchronized(this){





this.notify();





}





}





});






//显示图片的线程





Thread displayThread =new Thread(new Runnable(){





public void run(){







try{





/**





* 当显示线程通过调用一个对象的wait方法后





* 那嘛这个线程就在当前线程上等待,进入阻塞





* wait 等待阻塞 与睡眠阻塞的区别在于:





* sleep()会在指定的时间消耗完毕后自动回到Runnable状态





*





* wait() 阻塞不会自动回到Runnable,直接调用了这个对象的notify





* ()方法,当前线程才会到runnable





*





*/





synchronized(downLoadThread){





/**





* 当前线程在哪个对象上等待,就需要获取那个对象的锁





*/





downLoadThread.wait();}





}catch(InterruptedException e){





e.printStackTrace();





}





// if(!finish){


//


throw new RuntimeException(“图片没有下载完成!”);

//


}





System.out.println(“开始显示图片!”);





}





});






displayThread.start();





downLoadThread.start();

}

}




需求:从浏览器中发送请求:请求的参数为10个id的值,如何把这10个id分成5个线程进行查询


思路:


contoller ids 1,2,3,4,5,6,7,8,9,10(controller中的值是从url中传入的)

service:


1. ids/5     1,2 3,4 5,6 7,8 9,10


2. hql from t where id in(1,2)





hql from t where id in(3,4)





hql from t where id in(5,6)





hql from t where id in(7,8)





hql from t where id in(9,10)

3. 5个线程,1个线程传递一个hql,执行查询


基础知识:


java创建线程的三种方式:Callable,Runnable,Thread比较及用法


第一种方式:继承Thread类





GetName()方法返回调用该方法的线程的名字。



public class MyThread extends Thread{


int i=0;

public  void run(){





for(int i=0; i<100;i++){





System.out.println(getName()+” “+i);





}

}

public static void main(String[] args) {





Thread t1=new MyThread();





Thread t2=new MyThread();





t1.start();





t2.start();

}

}



第二种方法:实现Runnable接口


(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。


(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。


(3)调用线程对象的start()方法来启动该线程。





  1. public




    class


    RunnableThreadTest


    implements


    Runnable




  2. {







  3. private




    int


    i;





  4. public




    void


    run()




  5. {




  6. for


    (i =


    0


    ;i <


    100


    ;i++)




  7. {



  8. System.out.println(Thread.currentThread().getName()+

    ” ”


    +i);




  9. }



  10. }




  11. public




    static




    void


    main(String[] args)




  12. {




  13. for


    (


    int


    i =


    0


    ;i <


    100


    ;i++)




  14. {



  15. System.out.println(Thread.currentThread().getName()+

    ” ”


    +i);





  16. if


    (i==


    20


    )




  17. {



  18. RunnableThreadTest rtt =

    new


    RunnableThreadTest();





  19. new


    Thread(rtt,


    “新线程1”


    ).start();





  20. new


    Thread(rtt,


    “新线程2”


    ).start();




  21. }



  22. }





  23. }





  24. }


第三种方式:




通过Callable和Future创建线程











1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。




(2)创建线程池,创建有多个返回值的任务,也就是说把返回值的任务放到一个集合中即:集合的泛型为Future类型。




(3)创建接口实现类的对象,往往在创建的对象的过程中构造方法需要传入要操作的属性值




(4)执行任务并获得Future对象,此对象的获得是通过线程池对象调用submit方法获得的,


把Future对象添加到集合中。




如果创建多个返回值的任务:红色的部分是必不可少的。



(5)关闭线程池,Future对象调用f.get()方法获取任务的返回值。



使用线程池时call方法的执行是在submit方法执行时开始执行。


@SuppressWarnings(“unchecked”)


public class CallableThreadTest{


public static void main(String[] args) throws ExecutionException,InterruptedException {

System.out.println(“—-程序开始运行—-“);





Date date1 = new Date();



int taskSize = 5;

// 创建一个线程池





ExecutorService pool = Executors.newFixedThreadPool(taskSize);

// 创建多个有返回值的任务





List<Future> list = new ArrayList<Future>();





for (int i = 0; i < taskSize; i++) {

Callable c = new MyCallable(i+””);





// 执行任务并获取Future对象





Future f = pool.submit(c);



list.add(f);





}

// 关闭线程池





pool.shutdown();

// 获取所有并发任务的运行结果





for (Future f : list) {

// 从Future对象上获取任务的返回值,并输出到控制台





System.out.println(“>>>” + f.get().toString());

}





}





}

class MyCallable implements Callable<Object> {





private String   taskNum;

MyCallable(String  string) {

this.taskNum = string;

}



public Object call() throws Exception {

System.out.println(“>>>” + taskNum + “任务启动”);





Date dateTmp1 = new Date();





Thread.sleep(1000);

Date dateTmp2 = new Date();





long time = dateTmp2.getTime() – dateTmp1.getTime();





System.out.println(“>>>” + taskNum + “任务终止”);

return taskNum + “任务返回运行结果,当前任务时间【” + time + “毫秒】”;





}





}


书写的过程:


在main方法中或者service方法中定义线程池的大小,并且创建线程池;创建有多个返回值的任务;创建线程(创建的方式大同小异,往往都是通过增强for循环创建)for循环中的内容基本固定,唯一不同的是实现类的构造方法的内容,构造方法中的内容需要在类的属性中进行定义;


int taskSize = 5;

// 1)创建一个线程池





ExecutorService pool = Executors.newFixedThreadPool(taskSize);


// 2)创建多个有返回值的任务





List<Future> list = new ArrayList<Future>();


//3) 创建线程





for (int i = 0; i < taskSize; i++) {





Callable c = new MyCallable(i+””);





// 执行任务并获取Future对象





Future f = pool.submit(c);



list.add(f);





}


项目代码:


public List<CaptureLog> getFaceLogList(String ids,Integer currentPage,Integer pageSize, Long logStartTime, Long logEndTime,Integer timeType, String cameraIds, Integer gender, Integer minAge,Integer maxAge, Integer feature) {





String[] split = ids.split(“,”);





List<String> list = new ArrayList<String>();





for (String str1 : split) {





list.add(str1);





}





List<String> result = new ArrayList<String>();





String temp=null;





for (int i = 0; i < list.size(); i++) {





if(i%2==0){





temp=list.get(i)+”,”;





}





if (i%2!=0) {





temp+=list.get(i);





result.add(temp);





temp=null;





}





}





List<String> sqlList = new ArrayList<String>(result.size());





for(String str : result) {





String conditionResult = structureSearchCondition(str,logStartTime,logEndTime, timeType, cameraIds, gender, minAge, maxAge,feature,null);





sqlList.add(“FROM CaptureLog”+conditionResult);





}





int taskSize=5;





ExecutorService pool = Executors.newFixedThreadPool(taskSize);





// 创建多个有返回值的任务





List<Future> list1= new ArrayList<Future>();





//将查询到的结果(集合)放到安全的集合中





Vector vector = new Vector();





//创建线程





for(String sql : sqlList) {





Callable c = new ThreadQuery(sql, vector);





Future f = pool.submit(c);





list1.add(f);





}





return vector;





}


@SuppressWarnings(“rawtypes”)





class ThreadQuery implements Callable {





private Vector vector;





private String sql;





ThreadQuery(String sql, Vector vector)





{





this.vector = vector;





this.sql = sql;





}





@Override





public Object call() throws Exception {





Query query = entityManager.createQuery(sql);





List<CaptureLog> list=query.getResultList();





System.out.println(list);





vector.addAll(list);





return vector;





}





}






此线程的执行顺序:


main方法或者service方法–》从上到下开始执行当执行到submit方法时执行call方法的内容,再去执行main方法或sevice方法的内容。


带逗号的字符串从逗号分隔的情况:


String[] split = ids.split(“,”);



List<String> list = new ArrayList<String>();





for (String str1 : split) {





list.add(str1);





}





List<String> result = new ArrayList<String>();





String temp=null;





for (int i = 0; i < list.size(); i++) {





if(i%2==0){





temp=list.get(i)+”,”;





}





if (i%2!=0) {





temp+=list.get(i);





result.add(temp);





temp=null;






}





版权声明:本文为yangshaojun1992原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。