上一篇讲解了线程与线程池创建的一些方法,这期讲线程常用方法与线程池之间的区别。
上一篇中实现线程有继承Thread与实现Runnable接口两种。这里建议用Runnable,因为接口可以实现多继承,且只有一个run方法。
start方法与run方法:
start方法来启动线程,真正的实现了多线程,因为这是无需等待run()方法体里的代码是否执行完毕,它会继续执行下面的代码。当启动了start方法,就启动了一个线程,线程处于就绪状态,直到得到cpu时间片,就会执行run方法。当run方法体运行结束,这个线程也就结束。
run方法只是一个普通的方法,run方法体里面写了要执行的内容,直接调用run方法,程序中也只是有主线程一个线程,仍需等待run方法里面的方法体执行完毕后,才能继续执行下面的代码。这样就是去了并发的意义。
总结:start方法会开辟一条新线程,并且只能被调用一次(因为一个线程只能被初始化一次),当调用start时,线程会进入就绪阶段,当初始化完毕jvm会自动调用run方法来执行run方法里面的方法体。而run方法是由jvm直接调用的,他只是一个普通方法。如果我们没用启动线程(调用start),直接调用run方法,这样并不会开辟新的线程,而是运行在当前的线程中(即run方法所在的线程中),这样就违背了创建线程的初衷。
用户线程和守护线程的区别
二者其实基本上是一样的。唯一的区别在于JVM何时离开。
用户线程:当存在任何一个用户线程未离开,JVM是不会离开的。
守护线程:如果只剩下守护线程未离开,JVM是可以离开的。
常用方法:
getId():获取线程的id
getName():获取线程的名字
t.getPriority():获取线程优先级
t.isAlive():判断是否处于活跃状态
t.isDaemon()):是否为守护线程
getState():获取线程状态
isinterrupted:判断线程是否中断
setPriority():设置线程优先级
interrupt:用于中断线程,抛出interrupted异常
jion:如果当前线程中调用线程对象join,当前线程阻塞,直到线程对象的run执行完毕,当前线程阻塞结束。
wait: 当执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放锁,使得其他线程能够被访问,可以通过notify和notifyAll方法来唤醒。
yield:暂停当前执行的线程对象,并执行其他线程。就是当前线程交出cpu权限,让cpu及执行其他线程。不释放锁
sleep:当调用sleep方法时,会暂时让出cpu的执行权,等到时间到时会重新获得执行权,不会释放锁。
yield与sleep区别:yield不能控制具体的交出时间,并且yield只能让相同优先级的线程获取被cpu执行的机会,yield不会进入阻塞状态,而是就绪状态。
synchronized:同步监视器(俗称锁)
当两个并发的线程访问同一个对象中的这个加锁同步代码块时,同一时刻最多只有一个线程能执行这段代码。
下面我们来看一个案例:
众所周知:试衣间是不能两个异性同时进的!!!我们模拟两个人不认识的人同一时间在商场购买衣服,而商场只有一个试衣间。
public class Demo10 {
static Object o=new Object();
public static void main(String[] args) {
Thread t=new Thread("杨过"){
public void run(){
shoping(Thread.currentThread().getName());
}
};
Thread t1=new Thread("容么么"){
public void run(){
shoping(Thread.currentThread().getName());
}
};
t.start();
t1.start();
}
public static void shoping(String name){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"进入商场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"开始挑衣服");
synchronized (o) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "进入试衣间");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "走出试衣间");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"结账走人");
}
}
运行结果:
我们在试衣间部分上了锁也就是说,这两个进程不能同时访问试衣间的那段代码,必须要等一个线程执行完毕后另一个线程才能执行。也就是说这两个线程并发,两个线程执行什么,执行快慢都可以,但到了加锁同步代码块时,另一个线程必须等待那段代码执行完毕,另一个线程才能访问那段代码块。
CountDownLatch与CyclicBarrier:
CountDownLatch:
CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0时,await()方法会阻塞程序继续运行。CountDownLatch能够使一个线程等待其他线程完成各自的工作后再执行。
public class Demo10 {
public static void main(String[] args) {
CountDownLatch cdl=new CountDownLatch(3);
Thread t1=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"完毕");
cdl.countDown();
}
};
Thread t2=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"完毕");
cdl.countDown();
}
};
Thread t3=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"完毕");
cdl.countDown();
}
};
Thread thread=new Thread(){
public void run(){
try {
cdl.await();//等待阻塞 其他线程全部执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("火箭发射。。。。");
}
};
thread.start();
t1.start();
t2.start();
t3.start();
}
}
运行结果:
可以看到三个线程都结束后,最后的线程才执行。
CyclicBarrier:
CyclicBarrier的字面意思是可循环使用的屏障。它要做的事情是让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所以被屏障拦截的线程才会继续干活。每个线程调用await方法告诉CyclicBarrier我已经到达屏障,然后当前线程被阻塞。
public class Demo11 {
public static void main(String[] args) {
CyclicBarrier c=new CyclicBarrier(5);
for (int i=0;i<11;i++){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t=new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()+"来了");
try {
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"走了");
}
};
t.start();
}
}
}
运行结果:(太长了没截图。)
Thread-0来了
Thread-1来了
Thread-2来了
Thread-3来了
Thread-4来了
Thread-4走了
Thread-0走了
Thread-1走了
Thread-3走了
Thread-2走了
Thread-5来了
Thread-6来了
Thread-7来了
Thread-8来了
Thread-9来了
Thread-9走了
Thread-5走了
Thread-7走了
Thread-8走了
Thread-6走了
Thread-10来了
以上我们可以看到只有当线程到达5的时候才会继续执行,而当运行到Thread-10时循环到达最大次数,没有新的进程加入,所以不会继续执行了。
CountDownLatch与CyclicBarrier区别:
CountDownLatch:一个线程或者多个线程,等待另外N个线程完成某个事情之后才能执行。只有一个等待线程,其他N个线程都可以顺利执行完毕。(可以简单理解为发射火箭,必须等待其他工作完成才能发射)
CyclicBarrier:N个线程互相等待,任何一个线程完成之前,所有线程都必须等待。在最后一个未await之前,所有N个线程都不算完成,都处于阻塞状态。(可以简单理解为运动会百米赛跑,必须达到一定人数,裁判才会开枪)