结合上一篇同步异步,这篇理解线程操作。
1、新建线程。不止thread和runnable,Callable和Future了解一下
package com.thread; import java.text.SimpleDateFormat; import java.util.Date; 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; import java.util.concurrent.FutureTask; /** * 线程启动 * @author Administrator * */ public class StartThreadTest extends Thread{ @Override public void run(){ for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"i:"+i); } } public static void main(String[] args) throws InterruptedException { //继承 Thread 类的线程启动方式 StartThreadTest test = new StartThreadTest(); StartThreadTest test2 = new StartThreadTest(); System.out.println("开始执行线程1......"); test.start();test2.start(); test.join();test2.join(); //join方法会让main线程等待test、test2线程执行完,如果对执行结果不感冒,则不用join System.out.println("结束执行线程1......"); } //****************************************************************************************************** static class startThread2 implements Runnable{ @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("i:"+i); } } public static void main(String[] args) { //实现 Runnable 接口的线程启动方式 startThread2 thread2 = new startThread2(); Thread t = new Thread(thread2); t.start(); } } //****************************************************************************************************** /** * Callable 用于获取线程执行结果 * 多个Callable耗时是每个线程的总耗时 * @author Administrator * */ static class startThread3 implements Callable<String>{ @Override public String call() throws Exception { int result = 0; for (int i = 0; i < 3; i++) { Thread.sleep(1000); result += i; } return String.valueOf(result); } public static void main(String[] args) throws Exception { SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss"); System.out.println("开始时间:"+format.format(new Date())); Callable<String> call = new Callable<String>() { @Override public String call() throws Exception { int result = 0; for (int i = 0; i < 5; i++) { Thread.sleep(1000); result += i; } return String.valueOf(result); } }; ExecutorService service = Executors.newSingleThreadExecutor(); Future<String> f = service.submit(call); System.out.println(f.get()); service.shutdown(); System.out.println("时间1:"+format.format(new Date())); //实现 Callable 接口的线程启动方式 startThread3 thread3 = new startThread3(); System.out.println("开始执行线程......"); String result = thread3.call(); System.out.println("结束执行线程。result:"+result); //result:3 【线程执行完获取结果,会阻塞当前线程】 System.out.println("时间2:"+format.format(new Date())); //开始时间:05:04:21 //10 //时间1:05:04:26 //开始执行线程...... //结束执行线程。result:3 //时间2:05:04:29 } } //****************************************************************************************************** /** * Future 用于获取异步线程的结果,总耗时是最长耗时的线程 * @author Administrator * */ static class startThread4 { public static void main(String[] args) throws InterruptedException, ExecutionException { SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss"); System.out.println("开始时间:"+format.format(new Date())); //操作1:耗时3秒 FutureTask<String> futureTask = new FutureTask<>(new startThread3()); new Thread(futureTask).start(); //操作2:耗时5秒 Callable<String> call = new Callable<String>() { @Override public String call() throws Exception { int result = 0; for (int i = 0; i < 5; i++) { Thread.sleep(1000); result += i; } return String.valueOf(result); } }; FutureTask<String> futureTask2 = new FutureTask<>(call); new Thread(futureTask2).start(); System.out.println("操作1返回:"+futureTask.get()); System.out.println("操作2返回:"+futureTask2.get()); System.out.println("结束时间:"+format.format(new Date())); //开始时间:04:50:53 //操作1返回:3 //操作2返回:10 //结束时间:04:50:58 } } }
2、停止线程。volatile和interrupt各显神通
package com.thread; import java.text.SimpleDateFormat; import java.util.Date; /** * 关闭线程 * @author Administrator * */ public class StopThreadTest extends Thread { static int i = 0; static int j = 0; @Override public void run() { //同步操作确保i,j都自增1 synchronized (this) { i++; try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } j++; } } public static void main(String[] args) throws InterruptedException { StopThreadTest test = new StopThreadTest(); test.start(); Thread.sleep(1000); //Thread.stop():强制关闭对象获取的锁,破坏线程数据安全 test.stop(); test.join(); System.out.println("i:"+i+";j:"+j); //i:1;j:0 } static class volatileStopThreadTest extends Thread{ volatile boolean exitFlag = false; //volatile确保可见性 static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss"); @Override public void run() { System.out.println("run线程开始时间:"+format.format(new Date())); while(!exitFlag){ System.out.println("run方法进入时间:"+format.format(new Date())); //同步操作确保i,j都自增1 synchronized (this) { i++; try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } j++; } } System.out.println("run线程结束时间:"+format.format(new Date())); } public static void main(String[] args) throws InterruptedException { System.out.println("主线程开始时间:"+format.format(new Date())); volatileStopThreadTest test = new volatileStopThreadTest(); test.start(); Thread.sleep(1000); System.out.println("主线程休眠1秒:"+format.format(new Date())); test.exitFlag = true; test.join(); System.out.println("i:"+i+";j:"+j); //主线程开始时间:05:17:03 //run线程开始时间:05:17:03 //run方法进入时间:05:17:03 //主线程休眠1秒:05:17:04 【标志位exitFlag改了,run方法依然在执行,只是下次竞争到锁都不执行run】 //run线程结束时间:05:17:13 //i:1;j:1 【确保了线程数据安全】 } } static class interruptStopThreadTest extends Thread{ static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss"); @Override public void run() { System.out.println(Thread.currentThread().getName()+"-线程进入run开始时间:"+format.format(new Date())); while(true){ //判断线程是否已经被中断,如果被中断,则下次竞争时直接退出 if(Thread.currentThread().isInterrupted()){ System.out.println(Thread.currentThread().getName()+"-线程已被中断......,中断时间:"+format.format(new Date())); //退出while循环 break; } //同步操作确保i,j都自增1 synchronized (this) { i++; try { Thread.sleep(10000); System.out.println(Thread.currentThread().getName()+"-线程休眠10秒......"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("中断标志:"+Thread.currentThread().isInterrupted()); //阻塞过程中被中断,test.interrupt()方法设置的中断标识会立刻变成false,需要重新设置中断标识 Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().getName()+"-线程被中断了!"); } j++; } //打印: //Thread-0-线程进入run开始时间:10:01:34 //java.lang.InterruptedException: sleep interrupted // at java.lang.Thread.sleep(Native Method) // at com.thread.StopThreadTest$interruptStopThreadTest.run(StopThreadTest.java:103) //中断标志:false //Thread-0-线程被中断了! //Thread-0-线程已被中断......,中断时间:10:01:36 //i:1;j:1 【线程数据安全】 // synchronized (this) { // i++; // try { // for (int z = 0; z < 10; z++) { // if(z==9){ // int a = 1/0; // System.out.println("模拟异常"); // } // } // Thread.sleep(5000); //异常后的语句不会执行 // } catch (Exception e) { // e.printStackTrace(); // //线程执行过程中抛出了异常,通过设置中断标识,控制下次竞争 // Thread.currentThread().interrupt(); // System.out.println(Thread.currentThread().getName()+"-线程被中断了!"); // } // j++; // } //打印: //Thread-0-线程进入run开始时间:10:08:43 //Thread-0-线程被中断了! //java.lang.ArithmeticException: / by zeroThread-0-线程已被中断......,中断时间:10:08:43 // at com.thread.StopThreadTest$interruptStopThreadTest.run(StopThreadTest.java:126) //i:1;j:1 【线程数据安全】 } } public static void main(String[] args) throws InterruptedException { interruptStopThreadTest test = new interruptStopThreadTest(); test.start(); Thread.sleep(2000); test.interrupt(); //中断线程 【设置中断标志,通过isInterrupted()控制竞争】 test.join(); System.out.println("i:"+i+";j:"+j); } } }
3、线程挂起和继续执行。suspend和resume谨慎使用
package com.thread; /** * 线程挂起与继续执行 * 不推荐用: * 1、锁得不到释放,如果第二个线程先于第一个线程执行resume之前执行start,将造成锁永久等待 * 2、锁住的共享资源如果暗含同步方法,将导致其他线程使用该同步方法等待 * @author Administrator * */ public class SuspendANDresumeThreadTest extends Thread{ volatile int i = 0; @Override public void run(){ while(true){ synchronized(this){ i++; System.out.println(Thread.currentThread().getName()+"-i:"+i); } } } public static void main(String[] args) throws InterruptedException { SuspendANDresumeThreadTest test = new SuspendANDresumeThreadTest(); SuspendANDresumeThreadTest test2 = new SuspendANDresumeThreadTest(); test.start(); Thread.sleep(1); //test线程挂起后,占有的锁不会释放,导致test2线程一直处于阻塞态 test.suspend(); // test2.start(); // test.join(); //另外:查看println源码得知该方法是同步方法,在test占有锁的同时,导致println()也被占有,从而main线程无法执行下面的打印语句 System.out.println("该句无法打印!"); } }
4、等待线程结束和谦让。join和yield
package com.thread; import java.text.SimpleDateFormat; import java.util.Date; /** * join:当前线程等待执行join的线程执行完 * yield:执行yield的线程让出占用的锁 * @author Administrator * */ public class JoinANDyieldThreadTest extends Thread{ static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss"); @Override public void run(){ for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"-i:"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { JoinANDyieldThreadTest test = new JoinANDyieldThreadTest(); JoinANDyieldThreadTest test2 = new JoinANDyieldThreadTest(); // test.start(); // Thread.sleep(3000); // test.yield(); // test2.start(); //Thread-0-i:0 //Thread-0-i:1 //Thread-0-i:2 //Thread-0-i:3 【执行test.yield()后,test依然抢到了锁】 //Thread-1-i:0 //Thread-1-i:1 //Thread-0-i:4 test.setPriority(MIN_PRIORITY); //低优先级 test2.setPriority(MAX_PRIORITY); //高优先级 System.out.println("开始时间:"+format.format(new Date())); test.start(); Thread.sleep(3000); test.yield(); test2.start(); test.join(); test2.join(); //确保下面的打印在test和test2执行完后执行 System.out.println("结束时间:"+format.format(new Date())); //Thread-0-i:0 //Thread-0-i:1 //Thread-0-i:2 //Thread-0-i:3 【test设置低优先级依然抢到了锁】 //Thread-1-i:0 //Thread-0-i:4 //Thread-1-i:1 //Thread-0-i:5 } }
5、线程等待和唤醒。wait和notify
package com.thread; /** * wait 和 notify 设计生产者与消费者模式 * 锁的选用:线程共享的变量 * wait 和 notify时机的运用,确保先生产后消费 * @author Administrator * */ public class WaitANDnotifyThreadTest { private static Integer count = 0; // 数据仓库计数 private final static Integer FULL = 5; // 数据仓库最大存储量 private static String lock = "lock"; // 锁标识 // Object obj = new Object(); //锁标识 public static void main(String[] args) { new Thread(new Producer(), "product1").start(); new Thread(new Consumer(), "consumer1").start(); new Thread(new Producer(), "product2").start(); new Thread(new Consumer(), "consumer2").start(); } // 生产者 static class Producer implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } synchronized (lock) { while (count == FULL) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName() + "-produce:: " + count); // 唤醒lock锁上的所有线程 lock.notifyAll(); } } } } // 消费者 static class Consumer implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } synchronized (lock) { // 如果首次消费者竞争得到锁,进入后等待 while (count == 0) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; System.out.println(Thread.currentThread().getName() + "-consume:: " + count); lock.notifyAll(); } } } } }