程序、进程、线程之间的关系
     
    
   
    
     程序:一段静态的代码,静态的对象。
    
   
    
     进程:程序的一次执行过程,正在运行的程序。如:运行中的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关键字 来修饰一个方法或者一个代码块,来保证在同一个时刻最多只有一个线程
来执行代代码。
方式一:
同步代码块
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()方法来启动该线程。
    
   
    
    
   
- 
     
public
class
RunnableThreadTest
implements
Runnable
 - 
     
{
 - 
     
 - 
     
private
int
i;
 - 
     
public
void
run()
 - 
     
{
 - 
     
for
(i =
0
;i <
100
;i++)
 - 
     
{
 - 
     
System.out.println(Thread.currentThread().getName()+
” ”
+i);
 - 
     
}
 - 
     
}
 - 
     
public
static
void
main(String[] args)
 - 
     
{
 - 
     
for
(
int
i =
0
;i <
100
;i++)
 - 
     
{
 - 
     
System.out.println(Thread.currentThread().getName()+
” ”
+i);
 - 
     
if
(i==
20
)
 - 
     
{
 - 
     
RunnableThreadTest rtt =
new
RunnableThreadTest();
 - 
     
new
Thread(rtt,
“新线程1”
).start();
 - 
     
new
Thread(rtt,
“新线程2”
).start();
 - 
     
}
 - 
     
}
 - 
     
}
 - 
     
}
 
第三种方式:
    
     
      
       通过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;
   
    
    
   
    
     
      }