* 线程:程序执行的一条路径,一个进程可以包含多条线程
* 多线程并发执行可以提高程序的效率,可以同时完成多个工作
*
* JVM的启动至少启动了垃圾回收线程和主线程两个线程,所以是多线程;
*
* 多线程的实现方式有2中:
* 1. 继承Thread
* 1) 定义类继承Thread
* 2) 重写run方法
* 3) 把新线程要做的事写在run方法中
* 4) 创建线程对象
* 5) 开启新线程,内部会自动执行run方法
创建线程的第一种方式:继承Thread类
1 public static void main(String[] args) { 2 3 MyThread mt = new MyThread(); // 4) 创建线程对象 4 mt.start(); // 5) 开启新线程,内部会自动执行run方法 5 for(int i = 0; i < 3000; i++) { 6 System.out.println("bb"); 7 } 8 } 9 10 } 11 12 class MyThread extends Thread { // 1) 定义类继承Thread 13 public void run() { // 2) 重写run方法 14 for(int i = 0; i < 3000; i++) { // 3) 把新线程要做的事写在run方法中 15 System.out.println("aaaaaaaaaaaaaaaa"); 16 } 17 } 18 }
创建线程的第2中方式:实现Runnable接口 * 实现多线程的第2中方式,实现Runnable接口:
* 1. 创建类实现Runnable接口
* 2. 重写run方法,将要执行的代码写入run方法中
* 3. 创建自定义类的对象
* 4. 创建Thread类对象,将自定义对象传入
* 5. Thread类对象调用start方法开启新线程,内部会自动调用Runnnable中的run方法
1 public static void main(String[] args) { 2 3 MyRunnable mr = new MyRunnable(); 4 Thread t = new Thread(mr); 5 t.start(); 6 for(int i = 0; i < 3000; i++) { 7 System.out.println("bbb"); 8 } 9 } 10 11 } 12 13 class MyRunnable implements Runnable{ 14 15 @Override 16 public void run() { 17 for(int i = 0; i < 3000; i++) { 18 System.out.println("aaaaaaaaaaaa"); 19 } 20 } 21 22 }
两种方式的区别:
* 1. 查看源码的区别
* a. 继承Thread:由于子类重写了Thread类的run(),当调用start()方法时,直接找子类的run()方法
* b. 实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnabe的引用是否为空,
* 如果不为空,编译时看的是Runnable的run(),运行时执行的是子类的run()方法
*
* 2. 实现使用上的区别:
* 继承Thread:
* a.好处:可以直接使用Thread类中的方法,代码简单
* b.弊端:如果已经有了父类,就不能使用这种方法
* 实现Runnable接口
* a.好处:即使自己定义的线程类有了父类也没有关系,因为有了父类可以实现接口,而且接口是可以多实现的
* b.弊端:不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂
使用匿名内部类实现多线程的两种方式:
1 public static void main(String[] args) { 2 3 new Thread() { 4 public void run() { 5 for(int i = 0; i < 3000; i++) { 6 System.out.println("aaaaa"); 7 } 8 } 9 }.start(); 10 11 new Thread(new Runnable() { 12 public void run() { 13 for(int i = 0; i < 3000; i++) { 14 System.out.println("bb"); 15 } 16 } 17 }).start(); 18 }
给线程设置名称两种方式:
1. 使用构造方法
2. 使用setName()方法
1 public class Demo4_ThreadMethod { 2 3 /* 4 * 设置线程名字的两种方法: 5 * 1. 构造方法 6 * 2. setName()方式 7 */ 8 public static void main(String[] args) { 9 10 //demo1(); 11 //demo2(); 12 //demo3(); 13 Thread t = new Thread() { 14 public void run() { 15 System.out.println(this.getName() + "...ccc"); 16 } 17 }; 18 t.setName("王五"); 19 t.start(); 20 } 21 22 /** 23 * 通过setName()方法给线程名赋值 24 */ 25 public static void demo3() { 26 new Thread() { 27 public void run() { 28 this.setName("李四"); 29 System.out.println(this.getName() + "...bb"); 30 } 31 }.start(); 32 } 33 34 /** 35 * 通过构造方法给线程名称赋值 36 */ 37 public static void demo2() { 38 new Thread("张三") { 39 public void run() { 40 System.out.println(this.getName() + "...aaa"); 41 } 42 }.start(); 43 } 44 45 /** 46 * 线程的名称是默认从0 Thread-0 依次往后累加 47 */ 48 public static void demo1() { 49 new Thread() { 50 public void run() { 51 System.out.println(this.getName() + "...aaa"); //Thread-0...aaa 52 } 53 }.start(); 54 } 55 56 }
currentThread()方法和sleep()方法的使用:
1 public class Demo5_CurrentThread { 2 3 /* 4 * CurrentThread()方法用来获取当前正在执行的线程 5 * 6 * sleep()方法:会使线程暂时停一段时间 7 */ 8 public static void main(String[] args) { 9 10 new Thread() { 11 public void run() { 12 System.out.println(getName() + "...bbb"); 13 } 14 }.start(); 15 16 new Thread(new Runnable() { 17 public void run() { 18 Thread.currentThread().setName("李四"); 19 //由于Runnable接口没有setName() 和 getName()方法,所以需要用currentThread()方法获取当前线程 20 System.out.println(Thread.currentThread().getName() + "...aaa"); 21 } 22 }).start(); 23 } 24 25 }
守护线程:
1 public class Demo6_Daemon { 2 3 /* 4 * 守护线程 5 */ 6 public static void main(String[] args) { 7 8 Thread t1 = new Thread() { 9 public void run() { 10 for(int i = 0; i < 2; i++) { 11 System.out.println(getName() + "...aaaa"); 12 } 13 } 14 }; 15 16 Thread t2 = new Thread() { 17 public void run() { 18 for(int i = 0; i < 50; i++) { 19 System.out.println(getName() + "...bb"); 20 } 21 } 22 }; 23 24 t2.setDaemon(true); //设置t2为守护线程,当t1结束时t2也会随之结束 25 t1.start(); 26 t2.start(); 27 } 28 29 }
join()方法的使用:
1 public class Demo7_Join { 2 3 /* 4 * join()插队,等另外一个线程执行完毕后,再接着执行 5 * join(1)插队1毫秒,1毫秒后两个线程继续交替执行 6 */ 7 public static void main(String[] args) { 8 final Thread t1 = new Thread() { 9 public void run() { 10 for(int i = 0; i < 10; i++) { 11 System.out.println(getName() + "...aaa"); 12 } 13 } 14 }; 15 16 Thread t2 = new Thread() { 17 public void run() { 18 for(int i = 0; i < 10; i++) { 19 if(i==2) { 20 try { 21 //t1.join(); //匿名内部类要想调用局部变量,需要局部变量声明为final类型的 22 t1.join(1); //t1加入1毫秒,1毫秒后两个接着交替执行 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 } 27 System.out.println(getName() + "...bb"); 28 } 29 } 30 }; 31 t1.start(); 32 t2.start(); 33 } 34 }
同步的使用:
public class Demo8_Sychronized { /* * 同步代码块中需要传入的锁对象可以是任何对象,但是不能是匿名对象 * * 静态同步方法中的锁对象是类的字节码对象,如:demo.class */ public static void main(String[] args) { Object o = new Object(); while(true) { synchronized(o) { System.out.print("t"); System.out.print("e"); System.out.print("s"); System.out.print("t"); System.out.println(); } synchronized(o) { System.out.print("a"); System.out.print("b"); System.out.print("c"); System.out.print("d"); System.out.println(); } } } }
练习:
4个窗口同时售100张火车票
1 继承Thread类的方式 2 3 public class Demo9_Ticket { 4 5 /* 6 * 四个窗口同时卖出100张票 7 */ 8 public static void main(String[] args) { 9 10 new Ticket().start(); 11 new Ticket().start(); 12 new Ticket().start(); 13 new Ticket().start(); 14 } 15 16 } 17 18 class Ticket extends Thread{ 19 private static int ticket = 100; 20 //private static Object o = new Object(); 如果是使用这种成员变量的方式,需要声明为静态变量 21 public void run() { 22 while(true) { 23 synchronized(Ticket.class) { 24 if(ticket <= 0) { 25 break; 26 } 27 try { 28 Thread.sleep(10); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 System.out.println(getName() + "...第" + ticket-- + "张票"); 33 } 34 } 35 } 36 } 37 38 ------------------------------------------ 39 40 实现Runnable接口的方式: 41 42 public class Demo10_Ticket { 43 44 /* 45 * 使用实现Runnable接口进行四个窗口同步卖票 46 */ 47 public static void main(String[] args) { 48 49 Ticket1 t = new Ticket1(); 50 new Thread(t).start(); 51 new Thread(t).start(); 52 new Thread(t).start(); 53 new Thread(t).start(); 54 55 /*Ticket1 t1 = new Ticket1(); 56 Thread t2 = new Thread(t1); 57 t2.start(); 58 t2.start(); 59 t2.start(); 60 t2.start(); 61 一个线程只能启动一次,调用一次start()方法 62 */ 63 } 64 65 } 66 67 class Ticket1 implements Runnable{ 68 69 private int ticket = 100; 70 @Override 71 public void run() { 72 while(true) { 73 synchronized(this) { 74 if(ticket<=0) { 75 break; 76 } 77 try { 78 Thread.sleep(10); 79 } catch (InterruptedException e) { 80 e.printStackTrace(); 81 } 82 System.out.println(Thread.currentThread().getName() + "...第" + ticket-- + "张票"); 83 } 84 } 85 } 86 87 }