Java多线程:超详细的线程方法合集
1、线程基本方法
package com.lyq.java.thread; /* 1、怎么获取当前线程对象? Thread t = Thread.currentThread(); 返回值t就是当前线程。 2、获取线程对象的名字 String name = 线程对象.getName(); 3、修改线程对象的名字 线程对象.setName("线程名字"); 4、当线程没有设置名字的时候,默认的名字有什么规律?(了解一下) Thread-0 Thread-1 Thread-2 Thread-3 ..... */ public class ThreadTest01 { public void doSome(){ // 这样就不行了 //this.getName(); //super.getName(); // 但是这样可以 String name = Thread.currentThread().getName(); System.out.println("------->" + name); } public static void main(String[] args) { ThreadTest05 tt = new ThreadTest05(); tt.doSome(); //currentThread就是当前线程对象 // 这个代码出现在main方法当中,所以当前线程就是主线程。 Thread currentThread = Thread.currentThread(); System.out.println(currentThread.getName()); //main // 创建线程对象 MyThread2 t = new MyThread2(); // 设置线程的名字 t.setName("t1"); // 获取线程的名字 String tName = t.getName(); System.out.println(tName); //Thread-0 MyThread2 t2 = new MyThread2(); t2.setName("t2"); System.out.println(t2.getName()); //Thread-1\ t2.start(); // 启动线程 t.start(); } } class MyThread2 extends Thread { public void run(){ for(int i = 0; i < 100; i++){ // currentThread就是当前线程对象。当前线程是谁呢? // 当t1线程执行run方法,那么这个当前线程就是t1 // 当t2线程执行run方法,那么这个当前线程就是t2 Thread currentThread = Thread.currentThread(); System.out.println(currentThread.getName() + "-->" + i); //System.out.println(super.getName() + "-->" + i); //System.out.println(this.getName() + "-->" + i); } } }
2、线程休眠:Thread.sleep()
package com.lyq.java.thread; /* 关于线程的sleep方法: static void sleep(long millis) 1、静态方法:Thread.sleep(1000); 2、参数是毫秒 3、作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。 这行代码出现在A线程中,A线程就会进入休眠。 这行代码出现在B线程中,B线程就会进入休眠。 4、Thread.sleep()方法,可以做到这种效果: 间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。 */ public class ThreadTest02 { public static void main(String[] args) { // 让当前线程进入休眠,睡眠5秒 // 当前线程是主线程!!! /*try { Thread.sleep(1000 * 5); } catch (InterruptedException e) { e.printStackTrace(); }*/ // 5秒之后执行这里的代码 //System.out.println("hello world!"); for(int i = 0; i < 10; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); // 睡眠1秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
3、关于Thread.sleep()方法的一个面试题
package com.lyq.java.thread; /* 关于Thread.sleep()方法的一个面试题: */ public class ThreadTest03 { public static void main(String[] args) { // 创建线程对象 Thread t = new MyThread3(); t.setName("t"); t.start(); // 调用sleep方法 try { // 问题:这行代码会让线程t进入休眠状态吗? t.sleep(1000 * 5); // 在执行的时候还是会转换成:Thread.sleep(1000 * 5); // 这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠。 // 这样代码出现在main方法中,main线程睡眠。 } catch (InterruptedException e) { e.printStackTrace(); } // 5秒之后这里才会执行。 System.out.println("hello World!"); } } class MyThread3 extends Thread { public void run(){ for(int i = 0; i < 10000; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); } } }
4、终止线程的睡眠:t.interrupt()
package com.lyq.java.thread; /* sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么叫醒一个正在睡眠的线程?? 注意:这个不是终断线程的执行,是终止线程的睡眠。 */ public class ThreadTest04 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable2()); t.setName("t"); t.start(); // 希望5秒之后,t线程醒来(5秒之后主线程手里的活儿干完了。) try { Thread.sleep(1000 * 5); } catch (InterruptedException e) { e.printStackTrace(); } // 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。) t.interrupt(); // 干扰,一盆冷水过去! } } class MyRunnable2 implements Runnable { // 重点:run()当中的异常不能throws,只能try catch // 因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。 @Override public void run() { System.out.println(Thread.currentThread().getName() + "---> begin"); try { // 睡眠1年 Thread.sleep(1000 * 60 * 60 * 24 * 365); } catch (InterruptedException e) { // 打印异常信息 //e.printStackTrace(); } //1年之后才会执行这里 System.out.println(Thread.currentThread().getName() + "---> end"); // 调用doOther //doOther(); } // 其它方法可以throws /*public void doOther() throws Exception{ }*/ }
5、强行终止一个线程的执行:stop()
package com.lyq.java.thread; /* 在java中怎么强行终止一个线程的执行。 这种方式存在很大的缺点:容易丢失数据。因为这种方式是直接将线程杀死了, 线程没有保存的数据将会丢失。不建议使用。 */ public class ThreadTest05 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable3()); t.setName("t"); t.start(); // 模拟5秒 try { Thread.sleep(1000 * 5); } catch (InterruptedException e) { e.printStackTrace(); } // 5秒之后强行终止t线程 t.stop(); // 已过时(不建议使用。) } } class MyRunnable3 implements Runnable { @Override public void run() { for(int i = 0; i < 10; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
6、合理的终止一个线程的执行
package com.lyq.java.thread; /* 怎么合理的终止一个线程的执行。这种方式是很常用的。 */ public class ThreadTest06 { public static void main(String[] args) { MyRunable4 r = new MyRunable4(); Thread t = new Thread(r); t.setName("t"); t.start(); // 模拟5秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 终止线程 // 你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。 r.run = false; } } class MyRunable4 implements Runnable { // 打一个布尔标记 boolean run = true; @Override public void run() { for (int i = 0; i < 10; i++){ if(run){ System.out.println(Thread.currentThread().getName() + "--->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }else{ // return就结束了,你在结束之前还有什么没保存的。 // 在这里可以保存呀。 //save.... //终止当前线程 return; } } } }
7、关于线程的优先级
package com.lyq.java.thread; /* 了解:关于线程的优先级 */ public class ThreadTest07 { public static void main(String[] args) { // 设置主线程的优先级为1 Thread.currentThread().setPriority(1); /*System.out.println("最高优先级" + Thread.MAX_PRIORITY); System.out.println("最低优先级" + Thread.MIN_PRIORITY); System.out.println("默认优先级" + Thread.NORM_PRIORITY);*/ // 获取当前线程对象,获取当前线程的优先级 Thread currentThread = Thread.currentThread(); // main线程的默认优先级是:5 //System.out.println(currentThread.getName() + "线程的默认优先级是:" + currentThread.getPriority()); Thread t = new Thread(new MyRunnable5()); t.setPriority(10); t.setName("t"); t.start(); // 优先级较高的,只是抢到的CPU时间片相对多一些。 // 大概率方向更偏向于优先级比较高的。 for(int i = 0; i < 10000; i++){ System.out.println(Thread.currentThread().getName() + "-->" + i); } } } class MyRunnable5 implements Runnable { @Override public void run() { // 获取线程优先级 //System.out.println(Thread.currentThread().getName() + "线程的默认优先级:" + Thread.currentThread().getPriority()); for(int i = 0; i < 10000; i++){ System.out.println(Thread.currentThread().getName() + "-->" + i); } } }
8、让位:Thread.yield()
package com.lyq.java.thread; /* 让位,当前线程暂停,回到就绪状态,让给其它线程。 静态方法:Thread.yield(); */ public class ThreadTest08 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable6()); t.setName("t"); t.start(); for(int i = 1; i <= 10000; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } } class MyRunnable6 implements Runnable { @Override public void run() { for(int i = 1; i <= 10000; i++) { //每100个让位一次。 if(i % 100 == 0){ Thread.yield(); // 当前线程暂停一下,让给主线程。 } System.out.println(Thread.currentThread().getName() + "--->" + i); } } }
9、线程合并:t.join()
package com.lyq.java.thread; /* 线程合并 */ public class ThreadTest09 { public static void main(String[] args) { System.out.println("main begin"); Thread t = new Thread(new MyRunnable7()); t.setName("t"); t.start(); //合并线程 try { t.join(); // t合并到当前线程中,当前线程受阻塞,t线程执行直到结束。 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main over"); } } class MyRunnable7 implements Runnable { @Override public void run() { for(int i = 0; i < 10000; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); } } }
10、守护线程:setDaemon()
package com.lyq.java.thread; /* 守护线程 */ public class ThreadTest10 { public static void main(String[] args) { Thread t = new BakDataThread(); t.setName("备份数据的线程"); // 启动线程之前,将线程设置为守护线程 t.setDaemon(true); t.start(); // 主线程:主线程是用户线程 for(int i = 0; i < 10; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class BakDataThread extends Thread { public void run(){ int i = 0; // 即使是死循环,但由于该线程是守护者,当用户线程结束,守护线程自动终止。 while(true){ System.out.println(Thread.currentThread().getName() + "--->" + (++i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
11、生产者和消费者模式
package com.lyq.java.thread; import java.util.ArrayList; import java.util.List; /* 1、使用wait方法和notify方法实现“生产者和消费者模式” 2、什么是“生产者和消费者模式”? 生产线程负责生产,消费线程负责消费。 生产线程和消费线程要达到均衡。 这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。 3、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。 4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。 5、wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。 6、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。 7、模拟这样一个需求: 仓库我们采用List集合。 List集合中假设只能存储1个元素。 1个元素就表示仓库满了。 如果List集合中元素个数是0,就表示仓库空了。 保证List集合中永远都是最多存储1个元素。 必须做到这种效果:生产1个消费1个。 */ public class ThreadTest11 { public static void main(String[] args) { // 创建1个仓库对象,共享的。 List list = new ArrayList(); // 创建两个线程对象 // 生产者线程 Thread t1 = new Thread(new Producer(list)); // 消费者线程 Thread t2 = new Thread(new Consumer(list)); t1.setName("生产者线程"); t2.setName("消费者线程"); t1.start(); t2.start(); } } // 生产线程 class Producer implements Runnable { // 仓库 private List list; public Producer(List list) { this.list = list; } @Override public void run() { // 一直生产(使用死循环来模拟一直生产) while(true){ // 给仓库对象list加锁。 synchronized (list){ if(list.size() > 0){ // 大于0,说明仓库中已经有1个元素了。 try { // 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。 list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 程序能够执行到这里说明仓库是空的,可以生产 Object obj = new Object(); list.add(obj); System.out.println(Thread.currentThread().getName() + "--->" + obj); // 唤醒消费者进行消费 list.notifyAll(); } } } } // 消费线程 class Consumer implements Runnable { // 仓库 private List list; public Consumer(List list) { this.list = list; } @Override public void run() { // 一直消费 while(true){ synchronized (list) { if(list.size() == 0){ try { // 仓库已经空了。 // 消费者线程等待,释放掉list集合的锁 list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 程序能够执行到此处说明仓库中有数据,进行消费。 Object obj = list.remove(0); System.out.println(Thread.currentThread().getName() + "--->" + obj); // 唤醒生产者生产。 list.notifyAll(); } } } }
12、定时器:Timer
package com.lyq.java.thread; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /* 使用定时器指定定时任务。 */ public class TimerTest { public static void main(String[] args) throws Exception { // 创建定时器对象 Timer timer = new Timer(); //Timer timer = new Timer(true); //守护线程的方式 // 指定定时任务 //timer.schedule(定时任务, 第一次执行时间, 间隔多久执行一次); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date firstTime = sdf.parse("2020-03-14 09:34:30"); //timer.schedule(new LogTimerTask() , firstTime, 1000 * 10); // 每年执行一次。 //timer.schedule(new LogTimerTask() , firstTime, 1000 * 60 * 60 * 24 * 365); //匿名内部类方式 timer.schedule(new TimerTask(){ @Override public void run() { // code.... } } , firstTime, 1000 * 10); } } // 编写一个定时任务类 // 假设这是一个记录日志的定时任务 class LogTimerTask extends TimerTask { @Override public void run() { // 编写你需要执行的任务就行了。 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String strTime = sdf.format(new Date()); System.out.println(strTime + ":成功完成了一次数据备份!"); } }