java线程的五种状态:
创建,就绪(调用start以后),运行,阻塞(sleep,suspend,wait),死亡(run方法执行完毕或者调用stop方法,无法再进入就绪
java的线程通常由以下2种方法实现:
1. 继承Thread类,重写run()方法:
thread本质也是实现了Runnable的接口的一个实例,启动线程的唯一方法就是start()方法。
在这里,start() 方法会启动一个新线程然后执行run() 方法,调用start()以后并不是马上就触发run()方法,而是使该线程变为Runnable的状态
Class MyThread extends Thread{
public void run(){
System.out.println("重写run方法”);
}
}
Public class Test{
public static void main(String[] args){
MyThread t = new Mythread();
t.start();//开启线程
}
}
2.实现Runnable接口, 实现run()方法:
简单例子
public class Bank {
private static int money;
public int getMoney(){
return money;
}
public void saveMoney(int m){
synchronized (this) {
System.out.println("存钱后的总金额:"+(money+=m));
}
}
public void drawMoney(int m){
synchronized (this) {
Bank bank = new Bank();
if (bank.getMoney()<=0) {
System.out.println("没得钱,取个pi");
}else {
System.out.println("取钱后剩的总金额:"+(money-=m));
}
}
}
public static void main(String[] args) {
Man m1 = new Man(); //实例化
Women w = new Women(); //实例化
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
Thread t3 = new Thread(m1);
Thread t4 = new Thread(w);
Thread t5 = new Thread(w);
Thread t6 = new Thread(w);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class Man implements Runnable{ //实现runnable接口
private Bank bank = new Bank();
public void run() { //实现run()方法
int m = 100;
int i=0;
while (i<5) {
bank.saveMoney(m);
i++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Women implements Runnable{
private Bank bank = new Bank();
public void run() {
int m = 100;
int i=0;
//bank.getMoney()>0
while (i<5) {
bank.drawMoney(m);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
经典面试:
1. 什么是线程, 与进程的区别是什么? 为什么要使用多线程?
线程可以被看做轻量级的进程, 是程序的一个最小执行单元, 一个进程可以拥有多个线程,各个线程共享进程中的内存空间(代码段,数据段,堆空间)。 各个线程也有自己的栈空间,每个线程互不影响的并发执行。
多线程研发的好处:
- 减少程序响应时间,提高效率: 比如某个操作很耗时或者陷入长时间等待(例如网络等待响应)这时候键盘跟鼠标将不会有响应,如果用多线程把这个线程先挂起等待,可以使程序具备更好的交互性。
- 线程的创建跟切换开销比较小多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好
- 多cpu计算机本来就有多线程能力,要利用
2. 怎样实现多线程, 哪个好?
1. 继承Thread类重写run()方法 2.实现Runnable接口并实现run()方法 3. 实现Callable接口,重写call()方法, 会有返回值并允许抛出异常。
实现Runnable接口比较好,因为java只能单一继承, 继承了Thread类就无法继承其他类。
3. 解释一下守护线程, 跟用户线程的区别。
java提供了两种线程: 用户线程跟守护线程
# 守护线程的存在与否不影响进程, 只要用户线程全部终止,那么进程运行终止
# 守护线程必须再stat()方法调用之前,调用setDaemon(true)
# 守护线程用来在后台提供通用服务,优先级较低, 其他的跟用户线程几乎一样。
4. join() 方法的作用是什么?
join()方法使让调用该方法的线程在执行完run()方法后,再执行join()方法后面的代码, 让两个线程合并,用于实现同步功能。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
代码实现:
public static void main(String[] args) {
method01();
method02();
}
/**
* 第一种实现方式,顺序写死在线程代码的内部了,有时候不方便
*/
private static void method01() {
Thread t1 = new Thread(new Runnable() {
@Override public void run() {
System.out.println("t1 is finished");
}
});
Thread t2 = new Thread(new Runnable() {
@Override public void run() {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 is finished");
}
});
Thread t3 = new Thread(new Runnable() {
@Override public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3 is finished");
}
});
t3.start();
t2.start();
t1.start();
}
/**
* 第二种实现方式,线程执行顺序可以在方法中调换
*/
private static void method02(){
Runnable runnable = new Runnable() {
@Override public void run() {
System.out.println(Thread.currentThread().getName() + "执行完成");
}
};
Thread t1 = new Thread(runnable, "t1");
Thread t2 = new Thread(runnable, "t2");
Thread t3 = new Thread(runnable, "t3");
try {
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
5. Sleep() 跟wait() 的区别? yield() ?
Sleap() 是Thread 类的静态方法,是线程用来控制自身流程使其暂停一段时间,把执行机会让给其他线程。
wait() 是object类的方法,用于个线程之间的通信。这个方法会使拥有该对象锁的进程等待,直到其他线程调用notify或者notifyAll的时候菜醒来, 开发人员也可也给定时间自动醒来。
# 调用sleap() 跟wait()都会使当前线程暂停但是sleap 不会释放锁, wait会释放所占用的锁,让其同步的数据被别的线程所使用。Wait通常被用于线程间交互,sleep通常被用于暂停执行。
yield() 方法是让线程重新回到可执行状态,然后给优先级最高的线程以运行机会。 sleep() 必须抛出InterruptedException异常, yield不用
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- Thread常用方法有:start/stop/yield/sleep/interrupt/join等,他们是线程级别的方法,所以并不会太关心锁的具体逻辑。
- Object的线程有关方法是:wait/wait(事件参数)/notify/notifyAll,他们是对象的方法,所以使用的时候就有点憋屈了,必须当前线程获取了本对象的锁才能使用,否则会报异常。但他们能更细粒度的控制锁,可以释放锁。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------