1:多线程(理解)
(1)如果一个应用程序有多条执行路径,则被称为多线程程序。
进程:正在执行的程序。
线程:程序的执行路径,执行单元。
单线程:如果一个应用程序只有一条执行路径,则被称为单线程程序。
多线程:如果一个应用程序有多条执行路径,则被称为多线程程序。
举例:迅雷下载,360的管理界面。
单进程单线程:一个人点一桌菜
单进程多线程:多个人点一桌菜
多进程多线程:多个人点多桌菜
(2)多线程程序实现的两种方案:(掌握 步骤我们一起写,代码你写)
A:继承Thread类
a:自定义类MyThread继承Thread类。
b:重写run方法(),在这里面输出1-100的数据。
c:创建测试类MyThreadTest,在测试类中创建MyThread类的多个对象。
d:调用start() ,注意不是run()方法,此外线程开启必须在main方法中代码之前,因为main方法也是一个线程,里面的代码是顺序执行的。
代码体现:
public class MyThread extends Thread { public MyThread(){} public MyThread(String name) { super(name); } public void run() { for(int x=1; x<=100; x++) { System.out.println(getName()+"---"+x); } } } public class MyThreadTest { public static void main(String[] args) { MyThread my1 = new MyThread("三国志"); MyThread my2 = new MyThread("三国杀"); //my1.setName("三国志"); //my2.setName("三国杀"); my1.start(); // 注意,调用的是start()方法,而不是run方法 my2.start(); } }
B:实现Runnable接口(更好)
a:自定义类MyRunnable实现Runnable接口。
b:重写run方法(),在这里面输出1-100的数据。
c:创建测试类MyThreadTest,在测试类中创建MyRunnable类的一个对象。
d:在测试类创建Thread类的多个对象,并把MyRunnable类的一个对象作为构造参数传递。
用到的构造器:public Thread(Runnable target,
String name)
e:调用start()
代码体现:
public class MyRunnable implements Runnable { public void run() { for(int x=1; x<=100; x++) { System.out.println(Thread.currentThread().getName()+"---"+x); } } } public class MyRunnableTest { public static void main(String[] args) { MyRunnable my = new MyRunnable(); Thread t1 = new Thread(my,"斗地主"); Thread t2 = new Thread(my,"三国群英传2"); t1.start(); t2.start(); } }
(3)面试题:
A:如何启动一个线程
B:start()和run()方法的区别
C:线程随机性原理
D:线程的生命周期
E:线程的安全问题是怎么产生的,以及是如何解决的?
F:同步解决线程安全问题的方式有几种?
G:同步代码块的锁对象是谁?同步方法的锁对象是谁?
F:静态方法的锁对象是谁?
(4)几个方法
A、优先级(priority)
线程优先级代表了抢占CPU的能力。优先级越高,抢到CPU执行的可能性越大。(一般环境下效果不明显,优先级并非绝对的执行顺序。)
优先级相关方法:
public final void setPriority(int?newPriority) 优先级取值:1-10
public final int getPriority()
例子:cn.itcast2.demo
package cn.itcast2; /* * 线程优先级: * public final int getPriority()获取优先级 * public final void setPriority(int newPriority)更改优先级 */ public class Demo { public static void main(String[] args) { MyThread mt = new MyThread(); mt.setName("唐嫣"); // mt.setPriority(10); System.out.println(mt.getPriority()); MyThread mt2 = new MyThread(); mt2.setName("柳岩"); mt2.setPriority(1); mt.start(); mt2.start(); } }
package cn.itcast2; public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); } } }
B、暂停(yield)
暂停当前正在执行的线程对象,并执行其他线程。(效果不明显,如果想保证完成线程相互等待一次执行,需要使用到后边的等待唤醒机制 )
线程礼让相关方法:
public static void yield()
例子:cn.itcast2.demo4 cn.itcast2.MyThread1.java
package cn.itcast2; /* * 线程礼让: * 暂停当前正在执行的线程对象,并执行其他线程 * public static void yield() */ public class Demo4 { public static void main(String[] args) { MyThread1 myThread = new MyThread1(); myThread.setName("唐嫣"); MyThread1 myThread2 = new MyThread1(); myThread2.setName("高圆圆"); myThread.start(); myThread2.start(); } }
package cn.itcast2; public class MyThread1 extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); Thread.yield(); } } }
C、加入(join) cn.itcast2.demo3
等待该线程终止。 即当前线程等待调用join方法的线程执行结束后再执行
public final void join() throws InterruptedException
package cn.itcast2; /* * 加入线程: * public final void join() throws InterruptedException 等待该线程终止。 即当前线程等待调用join方法的线程执行结束后再执行 */ public class Demo3 { public static void main(String[] args) throws Exception { MyThread myThread = new MyThread(); myThread.setName("唐嫣"); MyThread myThread2 = new MyThread(); myThread2.setName("高圆圆"); myThread.setPriority(1); myThread.start(); myThread2.start(); myThread2.join(); //等待myThread2线程执行完以后,main方法所在的线程才会执行 for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
package cn.itcast2; public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); } } }
D、守护线程
设置守护线程的方法:
没有设置为守护线程的线程执行完毕,则程序停止运行
public final void setDaemon(boolean on)
注意:该方法必须在线程启动前调用
例子: cn.itcast2.demo5
package cn.itcast2; /* * 守护线程: * 设置守护线程的方法: * public final void setDaemon(boolean on) * 注意:该方法必须在线程启动前调用 */ public class Demo5 { public static void main(String[] args) { MyThread1 myThread = new MyThread1(); myThread.setName("唐嫣"); MyThread1 myThread2 = new MyThread1(); myThread2.setName("高圆圆"); myThread.setDaemon(true); //设置守护线程 myThread2.setDaemon(true);//设置守护线程 myThread.start(); myThread2.start(); for (int i = 0; i < 10; i++) { //主线程没有设置为守护线程,主线程执行完毕,则程序停止运行 System.out.println(Thread.currentThread().getName()+":"+i); } } }
package cn.itcast2; public class MyThread1 extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); Thread.yield(); } } }
E、线程睡眠(sleep)
指定线程休眠一定时间,进入等待状态。在该段时间结束后,线程重新可执行。
线程休眠相关方法:
public static void sleep(long?millis) throws InterruptedException
例子:cn.itcast2.demo2
package cn.itcast2; /* * 线程休眠: * public static void sleep(long millis) throws InterruptedException 将当前线程休眠,指定毫秒值 */ public class Demo2 { public static void main(String[] args) throws Exception { System.out.println("我困了"); MyThread mt = new MyThread(); mt.start(); Thread.sleep(3000l); System.out.println("我醒了"); } }
package cn.itcast2; public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); } } }
F、线程中断(interrupt)
中断线程。
stop方法已过时,通常使用interrupt方法。
中断线程相关方法:
public void interrupt() 被中断的线程会报被中断异常,这时需要使用try/catch语句解决相关问题,线程后代码仍然可以继续执行
public final void stop() (已过时) 直接停止线程,线程后代码无法被执行
例子:cn.itcast2.demo6 cn.itcast2.MyThread2
package cn.itcast2; /* * stop方法直接停止线程,方法过于暴力。直接导致sleep后的代码无法执行。 * 线程中断: * public void interrupt() 抛出异常,处理异常,后边代码继续执行 */ public class Demo6 { public static void main(String[] args) throws Exception { MyThread2 myThread = new MyThread2(); MyThread2 myThread2 = new MyThread2(); myThread.start(); myThread2.start(); Thread.sleep(3000); myThread.stop(); myThread2.interrupt(); } }
package cn.itcast2; public class MyThread2 extends Thread { @Override public void run() { System.out.println("我困了"); try { Thread.sleep(10000l); } catch (InterruptedException e) { System.out.println("谁把我叫醒了,真烦!"); } System.out.println("睡醒了"); } }
(5)案例:
卖票案例。例子:cn.itcast3.demo cn.itcast3.Ticket
package cn.itcast3; /* * 线程安全问题: * 多个窗口卖指定个数的票 * 3个窗口卖100张票 * 票:共享数据 * 窗口:线程 */ public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread thread = new Thread(ticket,"唐嫣"); Thread thread2 = new Thread(ticket,"柳岩"); Thread thread3 = new Thread(ticket,"高圆圆"); thread.start(); thread2.start(); thread3.start(); } }
package cn.itcast3; /* * 票类(线程执行的目标):执行买票动作 */ public class Ticket implements Runnable { int number = 100; //应该放在成员变量的位置,实现三个对象的数据共享 Object o = new Object(); @Override public void run() { while(true) { synchronized(o) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //如果有票,就卖票 if(number>0) { //卖票 System.out.println(Thread.currentThread().getName()+":"+number); number--; } } } } }
线程安全问题:
怎么产生的:
A:多线程环境
B:有共享数据
C:多条语句操作共享数据
怎么解决:
把C步骤给锁起来。
两种方案:
a:同步代码块
synchronized(锁对象) {
一个原子性操作
}
注意:几个线程需要使用相同的锁对象进行同步操作,使用不同的锁是无法完成同步操作的。
例子:cn.itcast3.demo cn.itcast3.Ticket
package cn.itcast3; /* * 线程安全问题: * 多个窗口卖指定个数的票 * 3个窗口卖100张票 * 票:共享数据 * 窗口:线程 */ public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread thread = new Thread(ticket,"唐嫣"); Thread thread2 = new Thread(ticket,"柳岩"); Thread thread3 = new Thread(ticket,"高圆圆"); thread.start(); thread2.start(); thread3.start(); } }
package cn.itcast3; /* * 票类(线程执行的目标):执行买票动作 */ public class Ticket implements Runnable { int number = 100; //应该放在成员变量的位置,实现三个对象的数据共享 Object o = new Object(); @Override public void run() { while(true) { synchronized(o) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //如果有票,就卖票 if(number>0) { //卖票 System.out.println(Thread.currentThread().getName()+":"+number); number--; } } } } }
b:同步方法
把synchronized添加到方法声明上。返回值前。例如:public synchronized void method() {}
例子:cn.itcast4.demo cn.itcast4.Ticket
package cn.itcast4; /* * 线程安全问题: * 多个窗口卖指定个数的票 * 3个窗口卖100张票 * 票:共享数据 * 窗口:线程 */ public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread thread = new Thread(ticket,"唐嫣"); Thread thread2 = new Thread(ticket,"柳岩"); Thread thread3 = new Thread(ticket,"高圆圆"); thread.start(); thread2.start(); thread3.start(); } }
package cn.itcast4; /* * 票类(线程执行的目标):执行买票动作 * * 同步方法: * 在方法上,返回值前,声明synchronized定义同步方法。 * 该方法的锁为所在对象。 */ public class Ticket implements Runnable { int number = 100; Object o = new Object(); @Override public void run() { while(true) { if(number%2==0) { //当票数为偶数时 synchronized(this) { //当前不变的对象,即ticket try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //如果有票,就卖票 if(number>0) { //卖票 System.out.println(Thread.currentThread().getName()+":"+number); number--; } } }else { method(); } } } public synchronized void method() { //在方法上,返回值前,声明synchronized定义同步方法。 //该方法的锁为所在对象。 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //如果有票,就卖票 if(number>0) { //卖票 System.out.println(Thread.currentThread().getName()+":"+number); number--; } } }
c.静态同步方法
将方法所在类作为默认所,即XX.class
例子:cn.itcast5.demo cn.itcast5.Ticket
package cn.itcast5; /* * 线程安全问题: * 多个窗口卖指定个数的票 * 3个窗口卖100张票 * 票:共享数据 * 窗口:线程 */ public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread thread = new Thread(ticket,"唐嫣"); Thread thread2 = new Thread(ticket,"柳岩"); Thread thread3 = new Thread(ticket,"高圆圆"); thread.start(); thread2.start(); thread3.start(); } }
package cn.itcast5; /* * 票类(线程执行的目标):执行买票动作 * * 静态同步方法: * 在方法上,返回值前,声明static synchronized定义静态同步方法。 * 该方法的锁为所在的类对象。 */ public class Ticket implements Runnable { static int number = 100; Object o = new Object(); @Override public void run() { while(true) { if(number%2==0) { synchronized(Ticket.class) { //类对像 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //如果有票,就卖票 if(number>0) { //卖票 System.out.println(Thread.currentThread().getName()+":"+number); number--; } } }else { method(); } } } public static synchronized void method() { //类对像 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //如果有票,就卖票 if(number>0) { //卖票 System.out.println(Thread.currentThread().getName()+":"+number); number--; } } }
(6)Java同步机制的优缺点:
a:优点:解决了多线程安全问题
b:缺点:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。对于一个简单操作,单线程速度更快。
(7)多线程的优缺点
多线程并不提高某个程序的执行速度,仅仅是提高了CPU的使用率。
图1:多线程示意图
图2:多线程现率高
图3:多线程状态图
2:单例设计模式(理解 面试)
(1)保证类在内存中只有一个对象。
(2)怎么保证:
A:构造私有
B:自己造一个对象
C:提供公共访问方式
(3)两种方式:
A:懒汉式(面试)
public class Student {
private Student(){}
private static Student s = null;
public synchronized static Student getStudent() {
if(s == null) {
s = new Student();
}
return s;
}
}
B:饿汉式(开发)
public class Student {
private Student(){}
private static Student s = new Student();
public static Student getStudent() {
return s;
}
}
(4)JDK的一个类本身也是单例模式的。
Runtime