多线程程序实现的方式1:
1、继承Thread
定义类继承Tread
重写run方法
把新线程要做的事写在run方法中
创建线程对象
开启新线程,内部会自动执行run方法
package com.thread;public class Demo1 { public static void main(String[] args) { MyThread mt = new MyThread(); //4、创建Thread类的子类对象 mt.start(); //5、开启线程 for (int i = 0; i < 1000; i++) { System.out.println("bbbb"); } }}class MyThread extends Thread { //1、继承T public void run() { //2、重写run方法 for (int i = 0; i < 1000; i++) { //3、将要执行的代码写在run方法中 System.out.println("aaaaaaaaaa"); } }}
多线程程序实现的方式2:
2、实现Runnable
定义类实现Runnable接口
实现run方法
把新线程要做的事写在run方法中
创建自定义的Runnable的子类对象
创建Thread对象,传入Runnable
调用start()开启新线程,内部会自动调用Runnable的run()方法
package com.thread;public class Demo2 { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); //4、创建Runnable的子类对象 Thread t = new Thread(mr); //5、将其当作参数传递给Thread的构造函数 t.start(); //6、开启线程 for(int i = 0; i < 1000; i++) { System.out.println("bb"); } }}class MyRunnable implements Runnable { //1、定义一个类实现Runnable接口 @Override public void run() { //2、重写run方法、 for (int i = 0; i < 1000; i++) { //3、将要执行的代码写在run方法中 System.out.println("ccccccccc"); } } }
两种方式的区别:
查看源码的区别:
a、继承Thread:由于子类重写了Thread类的run(),当调用start()是,直接找子类的run()方法
b、实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,
不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
匿名内部类实现线程的两种方式:
继承Thread类
实现Runnable接口
package com.thread;public class Demo3 { public static void main(String[] args) { new Thread() { //1、继承Thread类 public void run(){ //2、重写run方法 for (int i = 0; i < 1000; i++) { //3、将要执行的代码写在run方法中 System.out.println("aaaa"); } } }.start(); //4、开启线程 new Thread(new Runnable() { //1、将Runnable的子类对象传递给Thread的构造方法 public void run() { //2、重写run方法 for (int i = 0; i < 1000; i++) { //3、将要执行的代码写在run方法中 System.out.println("bbbbbb"); } } }).start(); //4、开启线程 }}
获取名字和设置名字:
1、获取名字
通过getName()方法获取线程对象的名字
2、设置名字
通过构造函数可以传入String类型的名字
通过setName(String)方法可以设置线程对象的名字
package com.thread;public class Demo4 { public static void main(String[] args) { //demo1(); Thread t1 = new Thread(){ public void run(){ //this.setName("张三"); System.out.println(this.getName() + "....aaaaaa"); } }; Thread t2 = new Thread(){ public void run(){ //this.setName("李四"); System.out.println(this.getName() + "....bbb"); } }; t1.setName("张三"); t2.setName("李四"); t1.start(); t2.start(); } public static void demo1() { new Thread("锋哥"){ public void run(){ System.out.println(this.getName() + "....aaaaaa"); } }.start(); new Thread("珊珊"){ public void run(){ System.out.println(this.getName() + "....bbb"); } }.start(); }}
获取当前线程的对象:
Thread.currentThread(),主线程也可以获取
package com.thread;public class Demo5 { public static void main(String[] args) { new Thread(){ public void run(){ System.out.println(getName() + "....aaaaa"); } }.start(); new Thread(new Runnable() { public void run() { //Thread.currentThread()获取当前正在执行的线程 System.out.println(Thread.currentThread().getName() + "...bbb"); } }).start(); Thread.currentThread().setName("我是主线程"); System.out.println(Thread.currentThread().getName()); }}
休眠线程:
Thread(毫秒,纳秒),控制当前线程休眠若干毫秒 1秒 = 1000毫秒 1秒 = 1000 * 1000 *1000纳秒
package com.thread;public class Demo6 { public static void main(String[] args) throws InterruptedException { // demo1(); new Thread() { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "....aaaaa"); } } }.start(); new Thread() { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "....bbbb"); } } }.start(); } public static void demo1() throws InterruptedException { for (int i = 20; i >= 0; i--) { Thread.sleep(1000); System.out.println("倒计时第" + i + "秒"); } }}
守护线程:
setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都执行结束后,自动退出
package com.thread;public class Demo7 { public static void main(String[] args) { Thread t1 = new Thread(){ public void run(){ for (int i = 0; i < 2; i++) { System.out.println(getName() + "...aaaaaaa"); } } }; Thread t2 = new Thread(){ public void run(){ for (int i = 0; i < 50; i++) { System.out.println(getName() + "...bbb"); } } }; t2.setDaemon(true); //设置为守护线程 t1.start(); t2.start(); }}
加入线程:
join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续
join(int),可以等待指定的毫秒之后继续
package com.thread;public class Demo8 { /** * join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续 */ public static void main(String[] args) { Thread t1 = new Thread(){ public void run(){ for (int i = 0; i < 10; i++) { System.out.println(getName() + "...aaaaaaa"); } } }; Thread t2 = new Thread(){ public void run(){ for (int i = 0; i < 10; i++) { if (i == 2) { try { //t1.join(); t1.join(1); //插队指定的时间,过了指定时间后,两条线程交替执行 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName() + "...bbb"); } } }; t1.start(); t2.start(); }}
同步代码块:
1、什么情况下需要同步
当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时就需要同步
如果两段代码是同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另外一段代码
2、同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块
多个同步代码块如果使用相同的锁对象,那么他们就是同步的
package com.thread;public class Demo9 { public static void main(String[] args) { final Printer p = new Printer(); new Thread() { public void run() { while (true) { p.print1(); } } }.start(); new Thread() { public void run() { while (true) { p.print2(); } } }.start(); }}class Printer { Demo d = new Demo(); public void print1() { synchronized (d) { //同步代码块,锁机制,锁对象是任意的 System.out.print("H"); System.out.print("e"); System.out.print("l"); System.out.print("l"); System.out.print("o"); System.out.print("\r\n"); } } public void print2() { //synchronized (new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象 synchronized (d) { System.out.print("酷"); System.out.print("狗"); System.out.print("\r\n"); } }}class Demo{};
同步方法:
使用 synchronized关键字修饰一个方法,该方法中所有的代码都是同步的
package com.thread;public class Demo10 { public static void main(String[] args) { final Printer2 p = new Printer2(); new Thread() { public void run() { while (true) { p.print1(); } } }.start(); new Thread() { public void run() { while (true) { p.print2(); } } }.start(); } }class Printer2 { Demo d = new Demo(); //非静态的同步方法的锁对象是神马? //非静态的同步方法的锁对象是this //静态的同步方法的锁对象是神马? //是该类的字节码对象 public static synchronized void print1() { //同步方法只需要在方法上加synchronized关键字即可 System.out.print("H"); System.out.print("e"); System.out.print("l"); System.out.print("l"); System.out.print("o"); System.out.print("\r\n"); } public void print2() { synchronized (Printer2.class) { System.out.print("酷"); System.out.print("狗"); System.out.print("\r\n"); } }}
线程安全问题:
多线程并发操作同一数据时,就有可能出现线程安全问题
使用同步技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作
package com.thread;public class Demo11 { /** * 需求:铁路售票,一共100张,通过四个窗口卖完 * @param args */ public static void main(String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); new Ticket().start(); }}class Ticket extends Thread { private static int ticket = 100; //private static Object obj = new Object(); //如果用引用数据类型成员变量当作锁对象,必须是静态的 public void run(){ while(true){ synchronized (Ticket.class) { if (ticket <= 0) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "...这是第" + ticket-- + "号票"); } } }}
火车站卖票的例子用实现Runnable接口:
package com.thread;public class Demo12 { /** * 需求:火车站卖票的例子用实现Runnable接口 * @param args */ public static void main(String[] args) { MyTicket mt = new MyTicket(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); /*Thread t1 = new Thread(mt); //多次启动一个线程是非法的 t1.start(); t1.start(); t1.start(); t1.start();*/ }}class MyTicket implements Runnable { private int tickets = 100; @Override public void run() { while(true){ synchronized (this) { if (tickets <= 0) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票"); } } } }
线程安全的类回顾:
看源码:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
Vector是线程安全的,ArrayList是线程不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
Hashtable是线程安全的,HashMap是线程不安全的