(1)继承 Thread 类,重写run方法
(2)实现 Runnable 接口,重写run方法
- public class Thread01 {
- public static void main(String[] args) {
- Cat cat = new Cat();
- cat.start();
- }
- }
-
-
-
- class Cat extends Thread{
-
- @Override
- public void run() {
- while (true){
- System.out.println("miaomiao");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
(1)java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承 Thread 类方法来创建线程显然不可能了
(2)java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
- public class Thread02 {
- public static void main(String[] args) {
- Dog dog = new Dog();
- Thread thread = new Thread(dog);
- thread.start();
- }
- }
-
- class Dog implements Runnable {
-
- @Override
- public void run() {
- while (true) {
- System.out.println("wangwang");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
(1)从 java 的设计来看,通过继承 Thread 或者实现 Runnable 接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到 Thread 类本身就实现了 Runnable 接口
(2)实现 Runnable 接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
public class Thread03 { public static void main(String[] args) { T3 t3 = new T3(); Thread thread1 = new Thread(t3); Thread thread2 = new Thread(t3); thread1.start(); thread2.start(); } } class T3 implements Runnable{ @Override public void run() { } }
(1)当线程完成任务后,会自动退出
(2)还可以通过使用变量来控制 run 方法退出的方式停止线程,即通知方式
public class ThreadExit_ { public static void main(String[] args) throws InterruptedException { T t1 = new T(); t1.start(); Thread.sleep(10*1000); t1.setLoop(false); } } class T extends Thread{ private boolean loop = true; @Override public void run() { while (loop){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T 运行中"); } } public void setLoop(boolean loop) { this.loop = loop; } }
(1)setName //设置线程名称
(2)getName //返回该线程的名称
(3)start //使该线程开始执行:Java虚拟机底层调用该线程的 start0 方法
(4)run //调用线程对象run方法
(5)setPriority //更改线程的优先级
(6)getPriority //获取线程的优先级
(7)sleep //在指定的毫秒数内让当前正在执行的线程休眠民(暂停执行)
(8)interrupt //中断线程
public class ThreadMethod { public static void main(String[] args) { T t = new T(); t.setName("线程1"); t.setPriority(Thread.MAX_PRIORITY); t.start(); t.interrupt(); System.out.println(t.getPriority()); } } class T extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()); while (true) { for (int i = 0; i < 10; i++) { System.out.println("任务执行中" + i); } try { Thread.sleep(2000); } catch (InterruptedException e) { // 当该线程执行了 interrupt 方法时,就会catch异常。这时可以加入自己的业务代码 // InterruptedException 捕获到了一个中断异常 System.out.println("被 interrupt了,业务执行中"); } } } }注意:
①start:底层会创建新的线程,调用 run,run 就是一个简单的方法调用,不会启动新
线程②interrupt:中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
(9)yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
(10)join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
(1)用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
(2)守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
(3)常见的守护线程:垃圾回收机制
public static void main(String[] args) { T t = new T(); t.setDaemon(true); t.start(); }
Thread.State 枚举类
(1)NEW:尚未启动的线程处于此状态
(2)RUNNABLE:在 Java 虚拟机中执行的线程处于此状态
(3)BLOCKED:被阻塞等待监视器锁定的线程处于此状态
(4)WAITING:正在等待另一个线程执行特定动作的线程处于此状态
(5)TIMED WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
(6)TERMINATED:已退出的线程处于此状态
(1)Java语言中,引入了对象互序锁的概念,来保证共享数据操作的完整性
(2)每个对象都对应于一个可称为“互序锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
(3)关键字 synchronized 来与对象的互斤锁联系。当某个对象用 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问
(4)同步的局限性:导致程序的执行效率要降低
(5)同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
(6)同步方法(静态的)的锁为当前类本身
注意事项和细节:
(1)同步方法如果没有使用 static 修饰:默认锁对象为 this
(2)如果方法使用 static 修饰,默认锁对象:当前类.class
(3)实现的落地步骤:
①需要先分析上锁的代码
②选择同步代码块(优先选择)或同步方法
③要求多个线程的锁对象为同一个即可
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生
(1)当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来(2)当前线程在同步代码块、同步方法中遇到break、return。
案例:没有正常的完事,经理叫他修改bug,不得已出来(3)当前线程在同步代码块、同步方法中出现了未处理的 Error 或 Exception,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来(4)当前线程在同步代码块、同步方法中执行了线程对象的 wait() 方法当前线程暂停,并释
放锁。
案例:没有正常完事,觉得需要际酿下,所以出来等会再进去
(1)线程执行同步代码块或同步方法时,程序调用 Thread.sleep()、Thread.yield() 方
法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上睡了一会(2)线程执行同步代码块时,其他线程调用了该线程的 suspend() 方法将该线程挂起
该线程不会释放锁
提示:应尽量避免使用 suspend() 和 resume() 来控制线程,方法不再推荐使用