一、多线程的实现方式
(一)第一种方式:继承方式
1、在
JDK
中有一个类是线程类:
Thread
2、操作步骤
(1)自定义一个类型继承
Thread
类
(2)在自定义类型中重写
Thread
类中的
run
方法,方法体就是将来线程的任务
(3)创建自定义类型对象,表示一个线程
(4)调用
start
方法,启动线程
public class Demo01_Thread {
public static void main(String[] args) {
//(3)创建自定义类型对象,表示一个线程
MyThread mt = new MyThread();
//(4)调用start方法,启动线程
mt.start();
for (int i = 0; i < 1000; i++) {
System.out.println(i +"main---");
}
}
}
//(1)自定义一个类型继承Thread类
class MyThread extends Thread {
//(2)在自定义类型中重写Thread类中的run方法,方法体就是将来线程的任务
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(i +"MyThread===================");
}
}
}
(二)第二种方式:实现方式
1、在
JDK
中有一个接口:
Runnable
2、步骤:
(1)自定义一个类,实现
Runnable
接口
(2)在自定义类中重写
Runnable
接口的
run
方法
(3)创建自定义类型的对象,表示一个任务
(4)创建线程对象,将任务添加到线程中
(5)线程对象调用
start
方法,启动线程
public class Demo02_Runnable {
public static void main(String[] args) {
//(3)创建自定义类型的对象,表示一个任务
MyTask mt = new MyTask
//(4)创建线程对象,将任务添加到线程中
Thread t = new Thread(mt);
//(5)线程对象调用start方法,启动线程
t.start();
for (int i = 0; i < 1000; i++) {
System.out.println(i + "main-");
}
}
}
//(1)自定义一个类,实现Runnable接口
class MyTask implements Runnable {
//(2)在自定义类中重写Runnable接口的run方法
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(i + "Runnable-----------------");
}
}
}
(三)匿名内部类简化多线程
public class Demo03_NoNameClass {
public static void main(String[] args) {
//第一种方式:继承
new Thread() {
@Override
public void run() {
System.out.println("继承方式中的执行了");
}
}.start();
//继承第二种写法
Thread t = new Thread() {
@Override
public void run() {
System.out.println("继承方式2执行了");
}
};
t.start();
//第二种方式:实现
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("接口中的方法执行了");
}
};
Thread th = new Thread(r);
th.start();
//实现第二种写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("接口中的方法2执行了");
}
}).start();
}
}
(四)两种方式比较
1、代码复杂程度
(1)继承方式比较简单
(2)实现方式比较复杂
2、实现原理
(1)继承方式:创建好线程之后,调用
start
方法,
start
方法又调用了
start0
方法,
start0
方法又调用了run方法,因为在自定义类型中重写了
Thread
父类中的
run
方法,根据重写的原则,直接执行子类的逻辑,我们重写的线程逻辑就执行了
(2)实现方式:创建接口的实现类对象,这个对象是一个任务对象。创建线程对象,将任务添加到线程对象中,将任务对象作为参数列表进行传递,线程对象在创建的过程中,连续调用了两个init
方法,之后,将任务对象作为线程对象的成员变量,赋值给线程。调用start
方法启动线程,
start
方法又调用了start0方法,
start0
方法又调用了
Thread
中的
run
方法,此时这个
run
方法执行,因为没有任何类型继承线程类,没有重写run
方法;先判断接口的声明引用是否赋值成功,如果成功接口的声明引用调用接口中的run
方法,因为接口中的
run
方法是抽象的,我们定义了自定义类型实现接口,并实现接口中的
run方法,此时执行的就是接口实现类中的run
方法,我们的业务逻辑就得到了实现。
3、设计
(1)继承方式:自定义类型继承
Thread
类型,就无法再去继承其它类型,代码扩展性较差
(2)实现方式:一个类在实现多个接口的同时还可以继承一个类型,代码扩展性较强
4、灵活性
(1)继承方式:将业务逻辑和线程对象绑定在一起了,耦合度高,灵活性差
(2)实现方式: 将任务和线程对象分开设计,降低耦合度,灵活性较强,一个任务对象可以被多个线执行,一个线程也可以执行不同的任务对象,并且将来可以将任务对象,提交到线程池中,任务对象被不同的线程执行,也方便多线程之间的数据交互
二、Thread常用方法
(一)获取线程名称
1、
getName
()
:获取线程名称
2、注意:
(1)如果没有给线程其任何名字,线程具有默认的名字,格式【
Thread-X
】
X
从
0
开始递增
(2)继承方式中,可以直接在run方法中调用getName,因为继承方式的run和getName方法都是在Thread父类中;实现方式的run方法是接口中的方法,getName是线程中的方法,所以不能直接调用
public class Demo04_GetName {
public static void main(String[] args) {
//继承方式
new Thread() {
@Override
public void run() {
System.out.println(getName());
}
}.start();
//实现方式
new Thread(new Runnable() {
@Override
public void run() {
// 接口中没有getName方法
// System.out.println(getName());
}
}).start();
}
private static void test01() {
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();
Thread t4 = new Thread();
System.out.println(t1.getName());
System.out.println(t2.getName());
System.out.println(t3.getName());
System.out.println(t4.getName());
}
}
(二)设置线程名称
1、
setName
(String name)
:设置线程名称
2、构造方法:
(1)
Thread
(String name)
:继承方式
(2)
Thread
(Runnable target, String name)
:实现方式
public class Demo05_SetName {
public static void main(String[] args) {
//继承方式
new Thread("继承赋值成功") {
@Override
public void run() {
System.out.println(getName());
}
}.start();
//实现方式
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
}
}, "实现方式");
System.out.println(t2.getName());
}
private static void test02() {
Thread t = new Thread() {
@Override
public void run() {
System.out.println(getName());
}
};
t.setName("继承");
System.out.println(t.getName());
t.start();
}
private static void test() {
Thread t1 = new Thread();
System.out.println(t1.getName());
t1.setName("新线程");
System.out.println(t1.getName());
}
}
(三)获取当前线程对象
1、
currentThread
()
:获取当前线程对象
2、作用:某段代码在执行,就一定会在某个线程中,此时如果调用获取线程对象的方法,该段代码,在哪条线程执行,就返回的是哪个线程的对象
public class Demo06_CurrentThread {
public static void main(String[] args) {
//继承
new Thread("线程111") {
@Override
public void run() {
System.out.println(Thread.currentThread());
}
}.start();
//实现
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}, "新线程").start();
System.out.println("00000");
}
}
(四)练习
获取垃圾回收线程的名称
public class Demo07_Exercise {
/**
* 获取垃圾回收线程的名称
*/
public static void main(String[] args) {
new Garbage();
//调用gc垃圾回收器,垃圾回收器会调用finalize方法
System.gc();
}
}
//子类重写父类的方法,执行子类的方法
class Garbage {
@Override
protected void finalize() throws Throwable {
System.out.println(Thread.currentThread().getName());
}
}
(五)线程休眠
1、
sleep
(long millis)
:让当前线程休眠指定的毫秒数
2、作用:当某个线程执行到该方法的时候,就让执行这个方法的线程休眠指定毫秒值
3、注意
(1)当在重写该方法的时候,如果重写的方法没有声明异常,重写后的方法只能
try...catch
(2)该方法会出现
InterruptedException
,中断异常,当线程休眠过程中被打断,会报此异常
public class Demo08_Sleep {
public static void main(String[] args) {
SleepThread st = new SleepThread();
Thread t1 = new Thread(st, "线程111");
Thread t2 = new Thread(st, "线程2222222222222222");
t1.start();
t2.start();
}
}
class SleepThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
if (Thread.currentThread().getName().equals("线程111")) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "@" + i);
} else {
System.out.println(Thread.currentThread().getName() + "@" + i);
}
}
}
}
(六)守护线程
1、概述:保证其它非守护线程能够正常执行,为其他非守护线程提供良好的运行环境,守护线程死亡,非守护线程正常执行;非守护线程死亡,守护线程就已经没有存在的意义了,片刻之后守护线程死亡
2、
setDaemon
(boolean on)
:将一个线程设置为守护线程
3、
isDaemon
()
:判断该线程是否为守护线程
public class Demo09_Daemon {
public static void main(String[] args) {
Thread t1 = new Thread("111") {
@Override
public void run() {
for (int i = 0; i < 2000; i++) {
System.out.println(i + "....................." + getName());
}
}
};
Thread t2 = new Thread("222") {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println(count + ".." + getName());
count++;
}
}
};
t2.setDaemon(true);
t1.start();
t2.start();
}
private static void test01() {
//任何线程出生之后默认非守护线程
Thread t1 = new Thread();
System.out.println(t1.isDaemon());
}
}
(七)线程优先级
1、概述:执行多线程的时候,每个线程都有优先级,优先级高的,在执行过程中相对靠前执行,优先级低的,相对靠后执行
2、三个优先级常量
(1)
MAX_PRIORITY
:最高优先级:
10
(2)
MIN_PRIORITY
:最低优先级:
1
(3)
NORM_PRIORITY
:默认优先级:
5
3、设置优先级的方法:setPriority(int newPriority)
public class Demo10_Priority {
public static void main(String[] args) {
PriorityThread pt = new PriorityThread();
Thread t1 = new Thread(pt, "111");
Thread t2 = new Thread(pt,"----------------------222");
t1.setPriority(10);
t2.setPriority(1);
t1.start();
t2.start();
}
}
class PriorityThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 2000; i++) {
System.out.println(i + "@" + Thread.currentThread().getName());
}
}
}
三、单例设计模式
(一)概述
1、设计模式:在发开中,针对类、接口、方法等设计的套路
2、在软件开发中,有
23
种设计模式,不同的需求中,可以使用不同的设计模式解决相关问题
3、单例设计模式:单例:单个实例,设计一个类,这个类有且仅有一个对象
4、步骤
(1)构造方法私有化
(2)在类中创建好对象
(3)对外提供公开的访问方式
(二)饿汉式
public class Demo11_SingletonHungry {
public static void main(String[] args) {
SingletonHungry sh1 = SingletonHungry.getInstance();
SingletonHungry sh2 = SingletonHungry.getInstance();
System.out.println(sh1);
System.out.println(sh2);
}
}
/**
* (1)构造方法私有化
* (2)在类中创建好对象
* (3)对外提供公开的访问方式
*/
class SingletonHungry {
//(1)构造方法私有化
private SingletonHungry() {}
//(2)在类中创建好对象
private static SingletonHungry sh = new SingletonHungry();
//(3)对外提供公开的访问方式
public static SingletonHungry getInstance() {
return sh;
}
}
(三)懒汉式
public class Demo08_SingletonLazy {
public static void main(String[] args) {
SingletonLazy i1 = SingletonLazy.getInstance();
SingletonLazy i2 = SingletonLazy.getInstance();
System.out.println(i1);
System.out.println(i2);
}
}
class SingletonLazy {
//(1)构造方法私有化
private SingletonLazy() {}
//(2)在类中创建好对象
private static SingletonLazy sl;
//(3)对外提供公开的访问方式
public static SingletonLazy getInstance() {
//3、多线程环境下如果每一次都要获取锁对象之后再去判断是否为空效率较低,外层加上一个判
断,尽可能的提高效率
if (sl == null) {
//2、多线程环境下,无法保证1的安全,所以加上同步代码块保证操作的完整性
synchronized (SingletonLazy.class) {
//1、判断当前对象是否存在,如果存在就不创建对象,不存在才创建
if (sl == null) {
sl = new SingletonLazy();
}
}
}
return sl;
}
}
(四)懒汉式简写
public class Demo12_SingletonLazy {
public static void main(String[] args) {
SingletonLazy sl = SingletonLazy.sl;
System.out.println(sl);
}
}
/**
* (1)构造方法私有化
* (2)在类中创建好对象
* (3)对外提供公开的访问方式
*/
class SingletonLazy {
//(1)构造方法私有化
private SingletonLazy() {}
//(2)在类中创建好对象
public static final SingletonLazy sl = new SingletonLazy();
}