线程和线程池
一、Thread 类
构造方法:Thread():创建新的线程
Thread(String name):创建新的线程并指定改线程名
Thread(Runnable runnable):创建新的线程并传入指定任务
常用方法:对象.start():开始线程并执行任务
run():要执行的任务
sleep(long millis):暂停多少毫秒后执行
实现过程:
1、自定义类使之继承 Thread 类
2、重写run()方法
3、创建自定义类对象
4、调用start()方法
Demo:
自定义类
1 public class MyThread extends Thread { 2 // run方法是用来描述线程任务的 3 @Override 4 public void run() { 5 for (int i = 0; i < 100; i++) { 6 System.out.println(getName() + ":" + i); 7 } 8 } 9 }
开启线程
1 public static void main(String[] args) { 2 MyThread mt = new MyThread(); 3 // 开启线程 4 mt.start(); 5 6 for (int i = 0; i < 100; i++) { 7 System.out.println( Thread.currentThread().getName()+":"+ i); 8 } 9 }
二、Runnable 接口 (创建任务对象)
好处:创建的任务对象,可以被多次执行,Thread 自定义类执行继承,继承的单一性,但是 Runnable 接口,可以实现多实现,不再单一。
实现过程:
1、自定义类使之实现 Runnable 接口
2、重写Run 方法
3、创建 自定义类的 对象
4、创建 Thread 对象 并传入 指定任务对象 (自定义类对象)
5、调用 Thread 的 start() 方法,使之开始执行任务
Demo
自定义类
1 public class MyRunnable implements Runnable { 2 3 @Override 4 public void run() { 5 for (int i = 0; i < 100; i++) { 6 System.out.println(Thread.currentThread().getName() + ":" + i); 7 } 8 } 9 }
开启线程
1 public static void main(String[] args) { 2 Thread.currentThread().setName("System"); 3 // 创建线程任务对象 4 MyRunnable mr = new MyRunnable(); 5 //创建线程对象,并传入任务 6 Thread t = new Thread(mr); 7 t.setName("YHYThread"); 8 //开启线程 9 t.start(); 10 for (int i = 0; i < 100; i++) { 11 System.out.println(Thread.currentThread().getName() + ":" + i); 12 } 13 }
三、使用匿名内部类来实现多线程
1、使用 Thread
new Thread(){
重写run 方法
}.start();
1 new Thread(new Runnable() { 2 public void run() { 3 Thread.currentThread().setName("YY"); 4 for (int i = 0; i < 100; i++) { 5 System.out.println(Thread.currentThread().getName() + ":" + i); 6 } 7 } 8 }).start();
2、使用 Runnable
Runnable r = new Runnable(){
重写run方法
};
Thread t = new Thread(r);
t.start();
1 public static void main(String[] args) { 2 Runnable r= new Runnable() { 3 @Override 4 public void run() { 5 System.out.println("任务"); 6 } 7 }; 8 9 Thread t= new Thread(r); 10 t.start(); 11 }
四、线程池
创建线程池对象:
ExecutorService es = Executors.newFixedThreadPool(int nThreads);//传入需要开始线程的数量。
使用Runnable 接口实现线程池
1、创建Runnable的任务对象 并重写 run() 方法。
2、获取线程池对象
3、线程池对象.submit(Runnable task);//传入要执行的任务对象
4、关闭线程池对象 线程池对象.shutdown();
1 public static void main(String[] args) { 2 // 指定任务对象,可以使用自定义类,实现Runnable接口 3 Runnable r = new Runnable() { 4 @Override 5 public void run() { 6 System.out.println("任务"); 7 } 8 }; 9 10 // 获取线程池对象 11 ExecutorService es = Executors.newFixedThreadPool(2); 12 // 指定任务 13 es.submit(r); 14 // 关闭线程池 15 es.shutdown(); 16 }
使用 Callable 接口实现线程池
1、创建 Callable 的任务对象,并重写 call() 方法。
2、创建线程池对象
3、线程池对象.submit(Callable c);//传入任务对象 (Callable 对象),假如有参数,使用Future<返回值类型> 接收
4、获取返回值 Future<返回值类型>对象.get() 获取 call() 方法的返回值
5、关闭线程池对象
1 public static void main(String[] args) throws InterruptedException, ExecutionException { 2 // 指定任务对象,并指定返回值,可以使用自定义类,实现Callable接口 3 Callable<String> c = new Callable<String>() { 4 @Override 5 public String call() throws Exception { 6 7 return "aaa"; 8 } 9 }; 10 11 // 获取线程池对象 12 ExecutorService es = Executors.newFixedThreadPool(2); 13 // 传入指定任务并使用 Future<V> 接收返回值 14 Future<String> submit = es.submit(c); 15 // Future<V>对象 的 get() 方法接收返回值 16 String str = submit.get(); 17 System.out.println(str); 18 // 关闭线程池 19 es.shutdown(); 20 }
Callable 比Runable 的好处是:可以抛异常,可以有返回值
五、线程安全
当线程任务共享一个数据源,并对这个数据进行操作的时候,有可能出现错误数据,这个时候就要用同步代码块,或者同步方法。
1、定义一个同步代码块
在 线程任务 方法外 定义任意对象 例如
1 package com.oracle.demo04; 2 3 4 public class Tickets implements Runnable { 5 6 // 所有线程共享数据放到成员位置 7 private int ticks = 100; 8 9 // 创建枷锁 10 Object lock = new Object(); 11 12 @Override 13 public void run() { 14 while (true) { 15 try { 16 Thread.sleep(500); 17 } catch (InterruptedException e) { 18 // TODO Auto-generated catch block 19 e.printStackTrace(); 20 } 21 synchronized (lock) { 22 if (ticks > 0) { 23 System.out.println(Thread.currentThread().getName() + "窗口卖了第:" + ticks-- + "张票"); 24 } 25 } 26 } 27 28 } 29 30 }
将有肯能影响数据的地方用 synchronized(object){ 问题代码 }
2、定义一个同步方法 用 synchronized 修饰的方法
1 package com.oracle.demo04; 2 3 public class Tickets2 implements Runnable { 4 // 所有线程共享数据放到成员位置 5 private int ticks = 100; 6 7 // 创建枷锁 8 Object lock = new Object(); 9 10 @Override 11 public void run() { 12 while (true) { 13 try { 14 Thread.sleep(500); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } 19 method(); 20 } 21 } 22 23 private synchronized void method() { 24 25 if (ticks > 0) { 26 System.out.println(Thread.currentThread().getName() + "窗口卖了第:" + ticks-- + "张票"); 27 } 28 } 29 }
然后在 线程任务中调用该方法。
3、使用 lock 类。控制 同步代码块
1 package com.oracle.demo04; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 public class Tickets3 implements Runnable { 7 8 // 所有线程共享数据放到成员位置 9 private int ticks = 100; 10 11 private Lock lock = new ReentrantLock(); 12 13 @Override 14 public void run() { 15 while (true) { 16 17 lock.lock(); 18 try { 19 Thread.sleep(500); 20 } catch (InterruptedException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 // 调用 lock 方法 获取锁 25 if (ticks > 0) { 26 System.out.println(Thread.currentThread().getName() + "窗口卖了第:" + ticks-- + "张票"); 27 } 28 if(ticks > 3) { 29 System.out.println(Thread.currentThread().getName() + "窗口卖了第:" + ticks-- + "张票"); 30 } 31 // 调用 unlock 方法 释放锁 32 lock.unlock(); 33 } 34 } 35 36 }