1:多线程
(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁
1 /* 2 * Lock: 3 * void lock():获取锁 4 * void lock():释放锁 5 * ReentrantLock的实现类 6 */ 7 public class SellTicketDemo { 8 public static void main(String[] args) { 9 //创建资源对象 10 SellTickets st = new SellTickets(); 11 12 //创建窗口 13 Thread t1 = new Thread(st, "窗口1"); 14 Thread t2 = new Thread(st, "窗口2"); 15 Thread t3 = new Thread(st, "窗口3"); 16 17 //启动线程 18 t1.start(); 19 t2.start(); 20 t3.start(); 21 } 22 23 }
1 public class SellTickets implements Runnable { 2 3 //定义票 4 private int tickets = 100; 5 6 //定义锁对象 7 private Lock lock = new ReentrantLock(); 8 9 @Override 10 public void run() { 11 while(true){ 12 try{ 13 //加锁 14 lock.lock(); 15 if(tickets > 0){ 16 try { 17 Thread.sleep(100); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 System.out.println(Thread.currentThread().getName() + "正在出售第:" + (tickets--) + "张票"); 22 } 23 }finally{ 24 //释放锁 25 lock.unlock(); 26 } 27 } 28 } 29 }
(2)死锁问题的描述和代码体现
同步的弊端:
效率低
容易产生死锁
死锁:两个或两个以上的线程在争夺资源的过程中发生的一种相互等待的现象。
1 public class DieLockDemo { 2 public static void main(String[] args) { 3 DieLock dl1 = new DieLock(true); 4 DieLock dl2 = new DieLock(false); 5 6 dl1.start(); 7 dl2.start(); 8 9 } 10 11 }
1 public class DieLock extends Thread { 2 3 private boolean flag; 4 5 public DieLock(boolean flag){ 6 this.flag = flag; 7 } 8 9 @Override 10 public void run() { 11 12 if(flag){ 13 synchronized (MyLock.objA) { 14 System.out.println("if objA"); 15 synchronized (MyLock.objB) { 16 System.out.println("if objB"); 17 } 18 } 19 }else{ 20 synchronized (MyLock.objB) { 21 System.out.println("if objB"); 22 synchronized (MyLock.objA) { 23 System.out.println("if objA"); 24 } 25 } 26 } 27 } 28 }
1 public class MyLock { 2 //创建两把锁 加static 是为了用类名调用 final防止变化 3 public static final Object objA = new Object(); 4 public static final Object objB = new Object(); 5 }
(3)生产者和消费者多线程体现(线程间通信问题)
以学生作为资源来实现的
资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo
代码:
A:最基本的版本,只有一个数据。
1 /* 2 * 资源类:Student 3 * 设置学生对象:SetThread(生产者) 4 * 获取学生对象:GetThread(消费者) 5 * 测试类:StudentDemo 6 */ 7 public class StudentDemo { 8 9 public static void main(String[] args) { 10 //创建资源 11 Student s = new Student(); 12 13 //设置和获取的类 14 SetThread st = new SetThread(s); 15 GetThread gt = new GetThread(s); 16 17 //线程类 18 Thread t1 = new Thread(st); 19 Thread t2 = new Thread(gt); 20 21 //启动 22 t1.start(); 23 t2.start(); 24 } 25 }
1 public class Student { 2 String name; 3 int age; 4 5 }
1 public class SetThread implements Runnable { 2 3 private Student s; 4 public SetThread(Student s){ 5 this.s = s; 6 } 7 @Override 8 public void run() { 9 //Student s = new Student(); 10 11 s.name = "张三"; 12 s.age = 20; 13 14 } 15 16 }
1 public class GetThread implements Runnable { 2 3 private Student s; 4 public GetThread(Student s){ 5 this.s = s; 6 } 7 @Override 8 public void run() { 9 //Student s = new Student(); 10 11 System.out.println(s.name + "---" + s.age); 12 13 } 14 15 }
B:改进版本,给出了不同的数据,并加入了同步机制
1 /* 2 * 资源类:Student 3 * 设置学生对象:SetThread(生产者) 4 * 获取学生对象:GetThread(消费者) 5 * 测试类:StudentDemo 6 */ 7 public class StudentDemo { 8 9 public static void main(String[] args) { 10 //创建资源 11 Student s = new Student(); 12 13 //设置和获取的类 14 SetThread st = new SetThread(s); 15 GetThread gt = new GetThread(s); 16 17 //线程类 18 Thread t1 = new Thread(st); 19 Thread t2 = new Thread(gt); 20 21 //启动 22 t1.start(); 23 t2.start(); 24 } 25 }
1 public class Student { 2 String name; 3 int age; 4 5 }
1 public class SetThread implements Runnable { 2 3 private Student s; 4 private int x = 0; 5 6 public SetThread(Student s){ 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 while(true){ 13 synchronized (s) { 14 if(x % 2 ==0){ 15 s.name = "张三"; 16 s.age = 20; 17 }else{ 18 s.name = "李四"; 19 s.age = 80; 20 } 21 x++; 22 } 23 24 } 25 } 26 27 }
1 public class GetThread implements Runnable { 2 3 private Student s; 4 public GetThread(Student s){ 5 this.s = s; 6 } 7 @Override 8 public void run() { 9 while(true){ 10 synchronized (s) {//必须使用同一把锁s 11 System.out.println(s.name + "---" + s.age); 12 } 13 14 } 15 } 16 17 }
C:等待唤醒机制改进该程序,让数据能够实现依次的出现
wait()
notify()
notifyAll() (多生产多消费)
1 /* 2 * 资源类:Student 3 * 设置学生对象:SetThread(生产者) 4 * 获取学生对象:GetThread(消费者) 5 * 测试类:StudentDemo 6 */ 7 public class StudentDemo { 8 9 public static void main(String[] args) { 10 //创建资源 11 Student s = new Student(); 12 13 //设置和获取的类 14 SetThread st = new SetThread(s); 15 GetThread gt = new GetThread(s); 16 17 //线程类 18 Thread t1 = new Thread(st); 19 Thread t2 = new Thread(gt); 20 21 //启动 22 t1.start(); 23 t2.start(); 24 } 25 }
1 public class Student { 2 String name; 3 int age; 4 5 boolean flag;//默认情况下是false 没有数据,如果是true 说明有数据 6 }
1 public class SetThread implements Runnable { 2 3 private Student s; 4 private int x = 0; 5 6 public SetThread(Student s){ 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 while(true){ 13 synchronized (s) { 14 //判断有没有 15 if(s.flag){//有 16 try { 17 s.wait(); 18 } catch (InterruptedException e) { 19 20 e.printStackTrace(); 21 } 22 } 23 24 25 if(x % 2 ==0){ 26 s.name = "张三"; 27 s.age = 20; 28 }else{ 29 s.name = "李四"; 30 s.age = 80; 31 } 32 x++; 33 34 //到这里已经有了数据,修改标记, 35 s.flag = true; 36 //唤醒线程 37 s.notify(); 38 } 39 40 } 41 } 42 43 }
1 public class GetThread implements Runnable { 2 3 private Student s; 4 public GetThread(Student s){ 5 this.s = s; 6 } 7 @Override 8 public void run() { 9 while(true){ 10 synchronized (s) {//必须使用同一把锁s 11 if(!s.flag){//没有 12 try { 13 s.wait(); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 } 19 System.out.println(s.name + "---" + s.age); 20 21 //修改标记 22 s.flag = false; 23 //唤醒线程 24 s.notify(); 25 26 } 27 28 } 29 } 30 31 }
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中
1 /* 2 * 资源类:Student 3 * 设置学生对象:SetThread(生产者) 4 * 获取学生对象:GetThread(消费者) 5 * 测试类:StudentDemo 6 */ 7 public class StudentDemo { 8 9 public static void main(String[] args) { 10 //创建资源 11 Student s = new Student(); 12 13 //设置和获取的类 14 SetThread st = new SetThread(s); 15 GetThread gt = new GetThread(s); 16 17 //线程类 18 Thread t1 = new Thread(st); 19 Thread t2 = new Thread(gt); 20 21 //启动 22 t1.start(); 23 t2.start(); 24 } 25 }
1 public class Student { 2 private String name; 3 private int age; 4 private boolean flag;//默认情况下是false 没有数据,如果是true 说明有数据 5 6 public synchronized void set(String name, int age){ 7 //如果有数据就等待 8 if(this.flag){ 9 try{ 10 this.wait(); 11 }catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 16 //设置数据 17 this.name = name; 18 this.age = age; 19 20 //修改标记 21 this.flag = true; 22 this.notify(); 23 } 24 public synchronized void get(){ 25 //如果没有数据就等待 26 if(!this.flag){ 27 try{ 28 this.wait(); 29 }catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 //获取数据 34 System.out.println(this.name + "----" + this.age); 35 } 36 }
1 public class GetThread implements Runnable { 2 3 private Student s; 4 public GetThread(Student s){ 5 this.s = s; 6 } 7 @Override 8 public void run() { 9 while(true){ 10 11 s.get(); 12 13 } 14 15 } 16 }
1 public class SetThread implements Runnable { 2 3 private Student s; 4 private int x = 0; 5 6 public SetThread(Student s){ 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 while(true){ 13 if(x % 2 ==0){ 14 s.set("张三", 20); 15 16 }else{ 17 s.set("李四", 30); 18 } 19 x++; 20 21 } 22 23 } 24 }
(4)线程组
1 /* 2 * 线程组:把多个线程组合到一起 3 *这样可以对一批 线程分类故管理,java允许程序直接对线程组进行控制 4 */ 5 public class ThreadGroupDemo { 6 public static void main(String[] args) { 7 method1(); 8 9 /* 10 * 创建一个线程组 11 * 创建其他线程的时候把其他线程组指定为我们自己新建的线程组 12 */ 13 method2(); 14 } 15 16 private static void method2() { 17 //ThreadGroup(String name) 18 ThreadGroup tg = new ThreadGroup("这是一个新的组"); 19 20 MyRunnable my = new MyRunnable(); 21 22 Thread t1 = new Thread(tg, my, "张三"); 23 Thread t2 = new Thread(tg, my, "李四"); 24 25 System.out.println(t1.getThreadGroup().getName()); 26 System.out.println(t1.getThreadGroup().getName()); 27 28 //通过组名称设置后台线程, 29 tg.setDaemon(true); 30 } 31 32 private static void method1() { 33 MyRunnable my = new MyRunnable(); 34 Thread t1 = new Thread(my, "张三"); 35 Thread t2 = new Thread(my, "李四"); 36 37 //线程类里面的方法:public final ThreadGroup getThreadGroup() 38 ThreadGroup tg1 = t1.getThreadGroup(); 39 ThreadGroup tg2 = t2.getThreadGroup(); 40 41 //线程组里面的方法:public final String getName() 42 String name1 = tg1.getName(); 43 String name2 = tg2.getName(); 44 System.out.println(name1); 45 System.out.println(name2); 46 System.out.println(Thread.currentThread().getThreadGroup().getName()); 47 //通过测试可以发现所有默认线程组都是main 48 49 }
1 public class MyRunnable implements Runnable{ 2 3 @Override 4 public void run() { 5 for(int x = 0; x < 100; x++){ 6 System.out.println(Thread.currentThread().getName()); 7 } 8 9 } 10 11 }
(5)线程池
为什么使用线程池:线程池中的每一个线程代码结束后并不会死亡,而是再次返回到线程池中成为空闲状体等待下一个对象来使用。
实现代码:创建一个线程池对象,控制要创建几个线程对象
public static ExecutorServer newFixedThreadPool(int nThreads)
线程池的线程可以执行下面:
执行Runnable对象或者Callable对象代表的线程
实现Runnable接口
调用如下方法:
Future<?> submit(Runnable tsk)
<T> Future<T> submit(Callable<T> task)
结束线程池
1 public class ExecutorsDemo { 2 3 public static void main(String[] args) { 4 //创建一个线程池对象,控制要创建几个线程对象 5 //public static ExecutorServer newFixedThreadPool(int nThreads) 6 ExecutorService pool = Executors.newFixedThreadPool(2); 7 8 //执行Runnable对象或者Callable对象代表的线程 9 pool.submit(new MyRunnable()); 10 pool.submit(new MyRunnable()); 11 12 //结束线程池 13 pool.shutdown(); 14 15 } 16 }
1 public class MyRunnable implements Runnable { 2 3 @Override 4 public void run() { 5 for(int x = 0; x < 100; x++){ 6 System.out.println(Thread.currentThread().getName() + ":" + x); 7 } 8 9 } 10 11 }
(6)多线程实现的第三种方案
1 public class CallableDemo { 2 3 public static void main(String[] args) { 4 //创建线程池对象 5 ExecutorService pool = Executors.newFixedThreadPool(2); 6 7 //执行Callable对象代表的线程 8 pool.submit(new MyCallable()); 9 pool.submit(new MyCallable()); 10 11 //结束 12 pool.shutdown(); 13 } 14 }
1 /* 2 * Callable:带泛型的接口,指定的泛型是call()方法的返回值类型 3 */ 4 public class MyCallable implements Callable { 5 6 //这里没有指定泛型就返回的是Object 7 @Override 8 public Object call() throws Exception { 9 for(int x = 0; x < 100; x++){ 10 System.out.println(Thread.currentThread().getName() + ":" + x); 11 } 12 return null; 13 } 14 15 }
1 /* 2 * 创建一个线程池对象,控制要创建几个线程对象 3 public static ExecutorServer newFixedThreadPool(int nThreads) 4 线程池的线程可以执行下面: 5 执行Runnable对象或者Callable对象代表的线程 6 实现Runnable接口 7 调用如下方法: 8 Future<?> submit(Runnable tsk) 9 <T> Future<T> submit(Callable<T> task) 10 结束线程池 11 */ 12 public class CallableDemo { 13 public static void main(String[] args) throws Exception, ExecutionException { 14 //创建线程池对象 15 ExecutorService pool = Executors.newFixedThreadPool(2); 16 17 //执行Callable对象代表的线程 18 Future<Integer> f1 = pool.submit(new MyCallable(1000)); 19 Future<Integer> f2 = pool.submit(new MyCallable(100)); 20 21 Integer i1 = f1.get(); 22 Integer i2 = f1.get(); 23 24 System.out.println(i1); 25 System.out.println(i2); 26 27 //结束 28 pool.shutdown(); 29 } 30 31 }
1 /* 2 * 线程求和 3 */ 4 public class MyCallable implements Callable<Integer> { 5 6 private int number; 7 public MyCallable(int number) { 8 this.number = number; 9 } 10 11 12 @Override 13 public Integer call() throws Exception { 14 int sum = 0; 15 for(int x = 1; x < number; x++){ 16 sum += x; 17 } 18 return sum; 19 } 20 21 }
匿名内部类的方法实现多线程
1 /* 2 * 匿名内部类格式: 3 * new 类或者接口名(){ 4 * 重写方法; 5 * }; 6 * 其本质是该类或者接口的子类对象 7 */ 8 public class ThreadDemo { 9 public static void main(String[] args) { 10 //继承Thread类来实现多线程 11 new Thread(){ 12 public void run() { 13 for(int x = 0; x < 100; x++){ 14 System.out.println(Thread.currentThread().getName() + ":" + x); 15 } 16 } 17 }.start(); 18 19 //实现Runnable接口实现多线程 20 new Thread(new Runnable() { 21 22 @Override 23 public void run() { 24 for(int x = 0; x < 100; x++){ 25 System.out.println(Thread.currentThread().getName() + ":" + x); 26 } 27 } 28 }){ 29 30 }.start(); 31 } 32 33 }
定时器与定时任务
可以在指定的时间做某件事情,还可以重复做某件事情
1 public class TimerDemo { 2 3 public static void main(String[] args) { 4 //创建定时器 5 Timer t = new Timer(); 6 //2秒后执行并结束任务 7 t.schedule(new MyTask(t), 2000); 8 } 9 } 10 11 //任务 12 class MyTask extends TimerTask{ 13 14 private Timer t; 15 16 public MyTask(){} 17 18 public MyTask(Timer t) { 19 this.t = t; 20 } 21 22 @Override 23 public void run() { 24 System.out.println("你好啊!"); 25 t.cancel(); 26 } 27 28 }
定时任务循环执行
1 public class TimerDemo { 2 3 public static void main(String[] args) { 4 //创建定时器 5 Timer t = new Timer(); 6 //2秒后执行任务第一次,如果不成功,每隔2秒再次执行 7 t.schedule(new MyTask(), 2000, 2000); 8 } 9 } 10 11 //任务 12 class MyTask extends TimerTask{ 13 14 @Override 15 public void run() { 16 System.out.println("你好啊!"); 17 18 } 19 20 }
定时删除指定带内容目录
1 class DeleteFolder extends TimerTask { 2 3 @Override 4 public void run() { 5 File srcFolder = new File("demo"); 6 7 DeleteFolder(srcFolder); 8 } 9 10 //递归删除目录 11 public void DeleteFolder(File srcFolder) { 12 File[] fileArray = srcFolder.listFiles(); 13 if (fileArray != null){ 14 for(File file : fileArray){ 15 if(file.isDirectory()){ 16 DeleteFolder(file); 17 }else{ 18 System.out.println(file.getName() + ":" + file.delete()); 19 } 20 } 21 System.out.println(srcFolder.getName() + ":" +srcFolder.delete()); 22 } 23 24 } 25 26 27 } 28 public class TimerTest{ 29 public static void main(String[] args) throws Exception { 30 Timer t = new Timer(); 31 32 String s = "2018-7-16 15:30:00"; 33 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd : HH:mm:ss"); 34 Date d = sdf.parse(s); 35 36 t.schedule(new DeleteFolder(), d); 37 } 38 39 } 40