多线程的创建
方式一:创建继承Thread类的子类
- 写法一:
- 1.创建一个继承于Thread类的子类
- 2.重写Thread类的run()方法 ----> 将创建线程需要执行的操作的声明在run()中
- 3.创建Thread的子类对象
- 4.通过子类对象调用start();{①启动当前线程 ②调用当前线程的run()}
- 5.启动多个线程,就要new多个线程对象
- 写法二:
- 创建Thread类匿名子类
new Thread() {
@Override
public void run() {
}
}.start();
Thread中的常用方法
* 1.start(); 启动当前线程,调用当前线程的run();\
* 2.run(); 通常需要重写Thread类中的此方法,将创建的线程需要执行的任务声明在此方法中
* 3.currentThread(); 返回当前代码所在的线程
* 4.getName(); 获取当前线程的名称
* 5.setName(); 设置当前线程的名称
* 6.yield(); 释放当前CPU执行权,让其他线程优先执行。
* 7.join() throws InterruptedException; 在线程 A 中调用线程 B 的join()方法,线程A进入阻塞状态,在线程B完全执行完毕后,线程A结束阻塞继续执行。
* 8.stop(); 已过时,执行此方法时,强制结束当前线程。
* 9.sleep(long millis); 让线程进入休眠状态,milltime单位为毫秒。
* 10.isAlive(); 判断当前线程是否还处于活跃状态。
*
* Thread的优先级
* 1.
* public final static int MIN_PRIORITY = 1;
* public final static int NORM_PRIORITY = 5;
* public final static int MAX_PRIORITY = 10;
*
* 2.如何获取和设置当前线程的优先级。
* 设置优先级setPriority(int newPriority); 数字越大,优先级越高;
* 获取优先级getPriority();
*
* 说明:高优先级线程的执行权 > 低优先级线程的执行权,
* 但是只是从概率上讲,并不意味着高优先级执行完毕才去执行低优先级的线程。
*
* 线程通信: wait(); notify(); notifyAll(); 这三个方法是定义在Object类中的。
创建多线程方式二: 实现Runnable接口
- 1.创建一个实现了实现Runnable接口接口的类
- 2.实现类去实现Runnable中的抽象方法run();
- 3.创建实现类对象
- 4.将此对象作为参数传递到Thread类的构造器中,创建Thread对象
- 5.通过Thread类的对象调用start(); 实际是调用了Runnable中的Run()方法
比较创建线程的两种方式
* 开发中:优先选择实现接口Runnable接口方式
* 原因:1.实现接口的方式,没有单继承的局限性
* 2.实现的方式更适合多个线程共用同一个数据情况
*
* 二者之间的联系:
* 1.Thread也实现了Runnable接口,也实现了Runnable中的run()方法
* public class Thread implements Runnable
* 2.两种实现方式都需要重写run()方法,并将线程要执行的逻辑代码放在run()中
守护线程和用户线程
- 二者唯一的区别是判断JVM何时离开
- 守护线程是用来服务用户线程的,通过在start()方法前调用 thread.setDaemon(true)可以把用户线程变成守护线程。
- Java垃圾回收的线程就是一个守护线程。
- 若JVM中都是守护线程,没有用户线程了,当前JVM将推出。
Java线程安全
- 同步的方式,解决了线程的安全问题。 ---- 坏处
- 操作同步代码块时,只能有一个线程参与,其他线程等待,相当于时一个单线程的过程,效率变低。---- 坏处
方式一:同步代码块
* synchronized(同步监视器){
* // 需要被同步的代码 -
* 1.操作共享数据的代码,即为需要被同步的代码
* 2.共享数据:多个线程共同操作的变量
* 3.同步监视器, 俗称:锁 任何对象都可以是同步锁
* 要求多个线程比如公用同一把锁
* 补充:实现runnable接口创建多线程的方式中,也可以考虑使用this作为同步锁
* 在使用继承Thread类创建多线程中,慎用this作为同步锁,因为可能会new多个对象
*
* 可以使用 class 作为同步锁,因为类在进程中只会加载一次。
* }
方式二:同步方法
- 1.如果操作数据的代码完整的声明在一个方法中,我们可以将此方法声明为同步的
- 2.非静态的同步方法,同步锁(同步监视器)为this
- 静态的同步方法,同步锁为this.getClass()
1.
* 使用实现Runnable接口的方法,可以把需要实现的同步代码块封装到一个方法中,并将方法声明为同步方法
* private synchronized void methodName() {
}
2. 使用继承Thread类方法,声明同步方法,把需要实现的同步代码块封装到一个方法中,并将方法声明为静态 同步方法
* private synchronized static void saleTicket() {
}
方式三:lock锁
private ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
try {
} finally {
reentrantLock.unlock();
}
synchronized 和 ReentrantLock 的异同
- 相同:
-
都是解决线程安全问题
- 不同:
-
synchronized是加锁后执行完相应的代码,自动释放线程锁
-
ReentrantLock是手动操作启动同步锁[lock()],手动解锁[unlock()]
-
ReentrantLock只有代码块锁,synchronized有代码块锁和方法锁
-
使用ReentrantLock,JVM将花费更少的时间来调度线程,性能更好,并具有更好的扩展性(提供了更多的子类)
- 优先使用顺序 ReentrantLock > 同步代码块 > 同步方法
线程死锁
- 理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方线程释放需要的同步资源,就行成了线程死锁。
- 说明:
1.出现死锁后,不回抛出异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
2.使用线程同步时,要避免线程死锁。