(1)、java命令会启动java虚拟机(jvm),相当于启动了一个应用程序,也就是启动了一个进程。然后该进程启动一个主线程,由主线程调用某个类的mian方法,所以main方法运行在主线程中的,前面的程序都是单线程程序。
(2)、jvm虚拟机启动是多线程的。因为垃圾回收线程也启动,否则就很容易出现内存溢出。一个主线程加上垃圾回收线程,至少都是2个线程,所以,jvm启动是多线程的。
(3)、线程的创建如何实现?
因为线程是依赖进程存在的,所以我们要先创建一个进程。进程是由系统创建的,所以我们要去调用系统功能创建一个进程,java是不能直接调用系统功能的。所以我们无法直接实现多线程程序。
但是,java可以去调用c/c++写好的程序来实现多线程程序。由c/c++去调用系统功能创建进程,然后java调用它。在java中提供了一些类来供我们使用。我们就可以实现多线程了
(3)、为什么要重写run方法?
不是说类中的所有代码都需要被线程执行的。所以,为了区分那些要被线程执行的代码,java就提供了Thread类,在这个类中提供了run()方法用来包含那些需要被线程执行的代码。
一般来说,被线程执行的代码肯定比较耗时的
(5)、常用方法
构造:new MyThread("119");设置线程名称
普通:public final String getName()
public final void setName(String name)
public static Thread currentThread();返回当前正在执行的线程对象
getName();获取所在的线程名称
Thread.currentThread().getName();
(6)、为什么默认的线程名称是 Thread-编号?
class Thread{
private String name;
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;//0
}
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//.....
}
public final String getName() {
return name;
}
}
(7)、调用main方法的线程的名称是什么?main
(8)、两种方式的对比
实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享。
联系:class Thread implements Runnable
实现的方式较好:①解决了单继承的局限性。②适合多个相同程序代码去处理同一个资源的情况。
(9)、售票程序模拟
public class Demo {
public static void main(String[] args) {
/* new TicketWindow().start();
new TicketWindow().start();
new TicketWindow().start();
new TicketWindow().start();
new TicketWindow().start();*/
//由于现实中铁路系统中的票资源是共享的,因此上述不合理,为了保证资源共享只能创建一个售票对象,然后开启多个线程去运行一个售票对象中的售票方法
TicketWindow m = new TicketWindow();
new Thread(m,"窗口1").start();
new Thread(m,"窗口2").start();
new Thread(m,"窗口3").start();
new Thread(m,"窗口4").start();
}
}
class TicketWindow extends Thread{
private int tickets = 100;
public void run() {
while(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread th = Thread.currentThread();
String name = th.getName();
System.out.println(name+"正在发售第"+tickets--+"张票");
}
}
}
//会出现负数票
(10)、线程的调度
线程调度有两种模型:分时调度模型和抢占式模型(Java虚拟机默认抢占式模型)
线程的优先级
最高优先级:static int MAX_PRIORITY (10)
最低优先级:static int MIN _PRIORITY (1)
默认优先级:static int NORM_PRIORITY (5)
线程的优先级控制
public final int getPriority() :返回线程优先值
public final void setPriority(int newPriority):改变线程的优先级
线程创建时继承父线程的优先级
(11)、多线程常用方法
Thread类下方法
setName(String name) | 更改线程名称 |
getName() | 返回此线程的名称 |
isAlive() | 测试这个线程是否活着,返回true和false |
join() | 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止 低优先级的线程也可以获得执行 |
stop() | 强制线程生命期结束 |
currentThread() | 返回当前线程,常用Thread.currentThread() |
public final void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程 |
public void interrupt() | 中断进程 |
Object类中涉及的线程操作方法
wait(long timeout) | 导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。 |
wait(long timeout, int nanos) | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或一定量的实时时间。 |
(12)、如下的三个方法必须使用在同步代码块或同步方法中!
wait():当在同步中,执行到此方法,则此线程“等待”,直至其他线程执行
notify()的方法,将其唤醒,唤醒后继续其wait()后的代码
notify()/notifyAll():在同步中,执行到此方法,则唤醒其他的某一个或所有的被wait的线程。
(13)、Java中的线程分为两类:一种是守护线程,一种是用户线程。
(14)、为什么wait()必须在同步(Synchronized)方法/代码块中调用?
答:调用wait()就是释放锁,释放锁的前提是必须要先获得锁,先获得锁才能释放锁。
(15)、为什么notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?
答:notify(),notifyAll()是将锁交给含有wait()方法的线程,让其继续执行下去,如果自身没有锁,怎么叫把锁交给其他线程呢;(本质是让处于入口队列的线程竞争锁)
(16)、synchronized与Lock的区别
首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
synchronized会自动释放锁,Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
(17)、死锁
同步的缺点:效率低,如果出现了同步嵌套,就会产生死锁问题。
死锁:指2个或2个以上的线程在执行的过程中,因为争夺资源产生的一种相互等待的现象。
public class Demo5 {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);
dl1.start();
dl2.start();
}
}
public class DieLock extends Thread{
private boolean boo;
public DieLock(boolean boo) {
this.boo = boo;
}
public void run() {
if(boo) {//t1
synchronized (MyLock.obj1) {
System.out.println("obj1");//t1
synchronized (MyLock.obj2) {
System.out.println("obj2");
}
}
}else {//t2
synchronized (MyLock.obj2) {
System.out.println("obj22");//t2
synchronized (MyLock.obj1) {
System.out.println("obj11");
}
}
}
}
}
public class MyLock {
public static Object obj1 = new Object();
public static Object obj2 = new Object();
}
(18)、线程组:把多个线程组合到一起
在java中可以使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,可以直接对线程组进行控制。默认情况下,所有的线程都属于主线程组
Thread类提供几个构造器来创建新的线程属于哪个线程组
Thread(ThreadGroup group,Runnable target):以target的run()方法为线程执行提创建新线程,新线程属于线程组group。
Thread(ThreadGroup group,Runnable target,String name):以target的run()方法为线程执行提创建新线程,并且输入group线程组,新线程名字为name。
Thread(ThreadGroup,String name):创建新线程,属于group线程组,新线程名字为name。
ThreadGroup类的简单构造器以及几个常用方法:
ThreadGroup(String name):创建名为name的新线程组。
ThreadGroup(ThreadGroup parent,String name):创建指定父线程组,名为name的新线程组。
int activeCount():返回此线程组中活动线程的数目。
interrupt():中断此线程组中所有的线程。
isDaemon():判断此线程组是否为后台线程。
setDaemon(boolean daemon):设置线程组为后台线程(当后台线程组中最后一个线程执行完成活着最后一个线程被销毁,后台线程将自动销毁)。
setMaxPriority(int prio):设置线程组的最高优先级。
线程对象关联线程组:
1级关联。介绍:1级关联就是父对象中有子对象,但并不创建子孙对象。
多级关联。介绍:多级关联就是父对象中有子对象,子对象中再创建子对象,也就是有子孙对象。如果关系太复杂反而不利于开发管理,所以多级关联不常见。
(19)、线程池
为提高程序的性能,效率。尤其当程序需要大量的生命周期比较短的线程时,就更应该考虑使用线程池。线程池中每一个线程在代码执行结束后,它不会死亡,而是会重新回到线程池中等待下一次被使用。
在java中有这么一个Executors工厂类可以生产线程池。
public static ExecutorService newCachedThreadPool(),创建有缓存功能的线程池
public static ExecutorService newFixedThreadPool(int nThreads),创建一个可以重用的,有指定线程数的线程池
public static ExecutorService newSingleThreadExecutor(),创建一个只有一个线程的线程池。
ExecutorService:这就是一个线程池对象,可以执行Runnable对象或Callable对象所表示的线程。
如何创建和使用线程池
创建线程池对象:ExecutorService threadPool = Executors.newFixedThreadPool(5);
可以执行Runnable对象或Callable对象所表示的线程:threadPool.submit(new MyThread());
关闭线程池:threadPool.shutdown();
(20)、定时器
是一个应用很广泛的工具,可以调度多个定时任务以后台线程的方式执行,java就提供了Timer 和 TimerTask 类来实现调度功能。
Timer
Timer() ,创建一个新计时器。 void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。 void schedule(TimerTask task, long delay) ,安排在指定延迟后执行指定的任务。
TimerTask
boolean cancel(),取消此计时器任务。 abstract void run(),此计时器任务要执行的操作
示例:
public static class Demo{
public static void main(String[]args){
TimerTask tsak = new TimerTask() {
@Override
public void run() {
System.out.println("秋招!必胜!");
}
};
Timer timer = new Timer();
timer.schedule(tsak,10000);
}
}