JAVA–线程问题
Thread中的常用方法:
1、start():启动当前线程,调用当前线程的run()
2、run():通常需要重写,将创建的线程要执行的操作声明在此方法中
3、currentThread()静态方法:返回执行当前代码的线程
4、getName():获取当前线程的名字
5、setName():设置当前线程的名字
6、yield():释放当前cpu的执行权(被其他线程组执行)
7、join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到线程b执行完毕后,线程a结束阻塞状态,继续执行
8、stop():强制结束线程 (已过时,不建议用)
9、sleep(传入参数,多少毫秒):强制线程阻塞,阻塞参数时间
10、isAlive():判断当前线程是否存活
线程的优先级
1、MAX_PRIORITY:10
MIN_PRIORITY:1
NROM_PRIORITY:5(默认优先级)
2、如何获取和设置当前县城的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
Tip:
高优先级的线程要抢占低优先级线程的cpu的执行权。但从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行
多线程的创建、
JDK5.0新增了两种线程创建方式,见下面
方法一 :
- 创建一个继承于Thread类的子类
- 重写run()类–>将此线程执行操作声明在run()中
- 创建Thread类的子类的对象
- 通过此对象调用start();
创建多线程的方式二:实现runnable接口
- 1、创建一个实现了runnable接口的类
- 2、实现类去实现runnable中的抽象方法:run();
- 3、创建实现类的对象
- 4、将此对象作为参数传递给Thread类的构造器中,创建Thread类中的额对象
- 5、通过Thread类的对象调用start()
比较创建线程的两种方式
开发中优先选择实现Runnable接口的方式
原因:1、实现的方式没有类的单继承的局限性
2、实现的方式更适合来出来多个线程有共享数据的情况联系 :public class Thread implements Runnable
相同点: 两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
线程的生命周期
线程的五种状态:新建、就绪、运行、阻塞、死亡
线程的同步(解决安全问题)
*车票就是共享数据
- 1、问题:卖票过程中出现了错票、重票现象
- 2、问题出现的原因: 当某个线程操作车票时,尚未操作完成时,其他线程参与尽力啊,也操作车票
- 3、解决:当一个线程a在操作车票时,其他线程不能参与进来除非线程a操作完成,
- 其他线程才可以开始操作。这样即使线程a出现了咋舌,也不能被改变
- 4、java中,我们通过同步机制解决线程安全问题
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:操作共享数据的代码,就是需要被同步带额代码
共享数据:多个线程共同操作的变量。车票就是
同步监视器(俗称锁):任何一个类的对象都可以充当锁
要求:多个线程必须同用一把锁
- 在实现Runnable接口创建多线程的方法中,我们可以考虑使用this充当同 步监视器
- 在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器
方式二:同步方法
- 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
1、同步方法仍然涉及到同步监视器,只不过不需要我们想显示声明
2、非静态的同步方法,同步监视器是this,静态的同步方法中,同步监视器是当前类的本身
同步方法解决了线程的安全问题
操作同步代码时,只能有一个线程参与,其他县城等待。相当于是一个单线程,效率低
- 有线程安全问题,就有共享数据,找到共享数据代码,同用一把锁
- 方式三、使用lock锁
- 1、实例化ReentrantLock
- 2、调用锁定方法:Lock()
- 3、调用解锁方法:unLock()
- synchronized与Lock异同:
- 同:都可以解决线程安全问题
- 不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器;Lock需要手动的启动同步(Lock()),同时结束同步也需要手动实现解锁(unLock())
使用顺序:Lock–> 同步代码块(synchronized,已经进入方法体,分配了相应资源)—>同步方法(在方法体之外)
class Window1 implements Runnable{
private int ticket =100;
Object obj = new Object();
@Override
public void run() {
while(true) {
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖票,票号:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t1= new Thread(w);
Thread t2= new Thread(w);
Thread t3= new Thread(w);
t1.setName("窗口1");
t2.setName("窗口3");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
线程的死锁问题
我们使用同步时,要避免死锁问题
线程的通信
线程通信的方法:
1、wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
2、notify():一旦执行此方法,就会唤醒wait的一个线程。如果有多个线程被wait,就先唤醒优先级高的
3、notifyall():一旦执行此方法,就会唤醒wait的所有线程
说明:1、这三个方法必须使用在同步代码块或同步方法中
2、这三个方法调用者必须是同步代码块或同步方法中的同步监视器,否则会出现异常
3、这三个方法是定义在java.lang.Object类中。
sleep()和wait()异同:
同:一旦执行方法,都可以使当前线程进入阻塞状态
不同:1、两个方法声明位置不不同:Thread类中国声明sleep(),Object类中声明wait()
2、调用的要求不同:sleep()可以再任何需要的场景下使用。wait()必须使用在同步代码块或同步方法中。
3、关于是否会释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放,wait()会释放
JDK5.0新增线程创建方式
方式一:实现Callable接口
与Runnable相比,Callable功能更强大
·相比run()方法,可以有返回值
·方法可以抛出异常
·支持泛型的返回执
·需要借助FutureTask类,比如获得返回结果
Future接口
1、创建一个实现Callable的实现类
2、实现Call方法,将此线程需要执行的操作声明在call()中
3、创建Callable接口实现类的对象
4、将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FUtureTask的的对象
5、讲FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
6、
方式二:使用线程池
·提高响应速度(减少了创建新线程的时间)
·降低资源消耗(重复利用线程池中线程,不需要每次都创建)
·便于线程管理