2.8 线程
2.8.1 线程的概念
程序:保存在物理介质中的代码片段
进程:正在进行当中的程序,就是操作系统的一个进程
线程:一个程序当中一条独立的执行线索
作用:只为让在同一时间做多件事,应对多种需求,服务多个客户,而不是单纯考虑提高效率
实质:宏观并行而围观串行
2.8.2 线程的五大进程(生命周期)
五大状态:
新生(NewBorn) 就绪(Runnable)运行(Running)阻塞(Blocking)消亡(Dead)
七大状态:
七大状态:新生(NewBorn) 就绪(Runnable)运行(Running)普通阻塞(Blocking)锁池阻塞 等待池阻塞 消亡(Dead)
2.8.3 实现线程的方式
-
extends Tread
class 线程1 extends Thread{ @Override public void run(){ } }//直接启动,线程1 x=new 线程1();x.start();
-
implements Runnable
xxxx implements Runnable{ @Override public void run(){ } } //自己造个Thread对象 传入Runnable对象做参数才能启动 /* 线程2 x=new 线程2(); Thread 对象1=new Thread(x); 对象1.start(); */
注意: 启动线程是调用start() 还是run()
答:start()
如果直接调用了run() ,程序当中根本不会多一路线程,而是当前调用run()的那个线程去执行run()当中的代码,等价于 将run()当中的指令,都复制到了当前线程中
2.8.4 控制线程
-
setPriority(int):设置线程的优先级别,可选范围1-10,默认为5,优先级高代表抢到时间片的概率高
-
static sleep(long):让当前的线程休眠指定毫秒数
-
static yield();让当前线程直接放弃时间片返回就绪【很可能自投自抢的情况,比较浪费CPU资源,建议少用】
-
join():让当前线程邀请调用方法的那个线程优先执行,在被邀请的线程执行结束之前当前线程一直处于阻塞状态,不再继续执行
*:线程类所有静态方法,不要关注谁调用了,而要关注调用的位置,出现在哪个线程体,该线程就执行该方法。
*:线程类所有涉及主动进入阻塞状态的方法(sleep() / join() / await() / wait())都必须进行异常处理,因为他们都有throws InterruptedException,这个是非运行时异常,必须处理。
*: 阻塞状态的线程 如何解除阻塞?
sleep() : 睡眠的时间超时了 就自动解除
join() : 被邀请的线程执行结束了 就自动解除
await() : 门闩都被拔掉的时候 就解除阻塞
wait() : 被其它线程notify()/notifyAll() 就解除阻塞
*: interrupt() 能够在这些条件都没满足的情况下 直接揍醒
进度条sleep
for(int i = 0;i<=100;i++){
System.out.print("\r已经完成"+i+"%");
Thread.sleep(100);
睡眠排序(升序)
public class TestSortedSleep{
public static void main(String[] args){
int[] data=new int[]{23,65,32,1,4,67,88,9,46};
for(int x:data){
Sleep sleep=new Sleep(x);
sleep.start();
}
}
}
class Sleep extends Thread{
int num;
public Sleep(int num){
this.num=num;
}
@Override
public void run(){
try{
Thread.sleep(num);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(num);
}
}
yield打印1-26和a-z
class NumThread extends Thread{
@Override
public void run(){
for(int i = 1;i<=26;i++){
System.out.println(i);
Thread.yield();
}
}
}
class CharThread extends Thread{
@Override
public void run(){
for(char c = 'a';c<='z';c++){
System.out.println(c);
Thread.yield();
}
}
}
join实例
public class TestJoin{
public static void main(String[] args)throws Exception{
EtoakThread et = new EtoakThread();
et.start();
/*
当前线程(主线程) 邀请 调用方法的线程(et)
优先执行 在et执行结束之前 主线程一直阻塞 不再继续执行
*/
et.join();
for(int i = 0;i<6666;i++){
System.out.println("梦回吹角连营");
}
}
}
class EtoakThread extends Thread{
@Override
public void run(){
for(int i = 0;i<6666;i++){
System.out.println("醉里挑灯看剑");
}
}
}
2.8.5 线程类其他常用方法:
setName():设置线程的名字
getName():得到线程的名字
static activeCount():得到程序中所有活跃的线程:就绪+运行+阻塞
setDaemon(true):设置线程成为守护线程(为了给别的线程提供服务的)
当程序当中只剩下守护线程的时候,守护线程会自行消亡(收尾工作 )
注意:
- 守护线程需要无限循环,防止其过早消亡
- 设置成为守护线程必须早于自己的start()
- 守护线程应该具有较低的优先级
interrupt():中断,打断线程中的阻塞状态
*:sleep()和join()都能导致线程阻塞
static currentThread():得到当前正在运行状态的线程对象
- 在主线程(main方法)当中,用于获得主线程对象,java当中唯一可以获得主线程对象的方式
- 在run()调用的其他方法中,用于获得当前线程
注意事项:不应该出现在run方法体中,因为得到的线程就是this
static activeCount案例
public class TestActiveCount{
public static void main(String[] args){
int x = (int)(Math.random()*3)+3;//3-5
for(int i = 0;i<x;i++){
EtoakThread et = new EtoakThread();
et.start();
}
System.out.println(Thread.activeCount());//包含主线程main
}
}
class EtoakThread extends Thread{
@Override
public void run(){
for(int i = 0;i<100;i++){
System.out.println("碾碎他们~");
}
}
}
setDaemon(true)案例
public class TestSetDaemon{
public static void main(String[] args){
GYJJ gy = new GYJJ();
gy.setDaemon(true);//必须早于自身的start();
gy.start();
gy.setPriority(1);//守护线程应当具有最低的优先级
for(int i = 0;i<100;i++){
System.out.println("西天取经上大路 一走就是几万里");
}
}
}
class GYJJ extends Thread{
@Override
public void run(){
//守护线程通常都是无限循环 以防止其过早消亡
while(true){
System.out.println("你这泼猴儿");
}
}
}
interrupt()案例
public class TestInterrupt{
public static void main(String[] args)throws Exception{
EtoakThread et = new EtoakThread();
et.start();
/*
主动出手打人的(主线程) 被动挨打的(et线程)
*/
et.interrupt();
}
}
class EtoakThread extends Thread{
@Override
public void run(){
try{
Thread.sleep(9999999999999999L);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("吖!神清气爽吖!");
}
}
static currentThread()案例
//放在主线程(main方法里面)用来获得主线程对象
public static void main(String[] args){
//让主线程的优先级 从默认的5 变成10
Thread zxc = Thread.currentThread();
zxc.setPriority(10);}
//放在run()调用的方法里面,用来获得当前线程对象
public class TestCase{
public static void main(String[] args){
Student stu=new Student("张三");
stu.start();
}}
class Student extends Thread{
public Student(String name){
setName(name);
}
@Override
public void run(){
System.out.println(getName()+"同学要问老师问题");
Teacher t1=new Teacher();
t1.answer();
}
}
class Teacher {
public void answer(){
Thread t = Thread.currentThread();
String name = t.getName();
System.out.println("老师认真回答"+name+"同学的问题");}}
CountDownLatch->替代连环try
方法:await():
countDown():
CountDown方法实战案例
import java.util.*;
import java.util.concurrent.*;
public class TestCurrentThread{
public static void main(String[] args) throws Exception{
Student stu1=new Student("小明");
Student stu2=new Student("小红");
Student stu3=new Student("小强");
stu1.start();
stu2.start();
stu3.start();
try{
X.cd.await();//学生线程结束,插上插销,进入等待
}catch(Exception e){
e.printStackTrace();
}
System.out.println("========账本=========");
Teacher tea=Teacher.getTea();
Map<Integer,String> temp=new TreeMap<>((a,b)->a.equals(b)?1:b.compareTo(a));
tea.map.forEach((name,count)->temp.put(count,name));
temp.forEach((count,name) -> System.out.println(name + " : " + count));//按照value降序排序
/* Set<Map.Entry<String,Integer>> es=tea.map.entrySet();
for(Map.Entry<String,Integer> jilu:es){
String name=jilu.getKey();
Integer count=jilu.getValue();
System.out.println(name+":"+count);
}*/
}
}
class X {
static CountDownLatch cd = new CountDownLatch(3);
}
class Student extends Thread{
public Student(String name){
setName(name);
}
@Override
public void run(){
System.out.println(getName()+"同学学习过程中遇到问题,想问老师");
Teacher tea=Teacher.getTea();
int x=(int)(Math.random()*10)+1;
for(int i=0;i<x;i++){
tea.answer();
}
X.cd.countDown();//访问完毕,拔掉插销,撤销等待
}
}
class Teacher extends Thread{
Map<String,Integer> map=new ConcurrentHashMap<>();
private Teacher(){}
private static Teacher tea=new Teacher();
public static Teacher getTea(){
return tea;}
public void answer(){
Thread t=Thread.currentThread();//获得调用该方法的线程对象
String name=t.getName();
System.out.println("认真解答"+name+"的问题");
if(map.containsKey(name)){
map.put(name,map.get(name)+1);
}else{
map.put(name,1);
}
}
}
利用可变参实现线程相继启动
public class Exec{
public static void main(String[] args){
//自定向下new对象,num的值也呈现5、4、3、2、1的变化
Print p5=new Print();//num=5,无线程要紧随其后启动
Print p4=new Print();//num=4,无线程要紧随其后启动
Print p3=new Print(p4,p5);//num=3,执行完成后需要启动p4和p5两个线程对象
Print p2=new Print(p3);//num=2,执行完成后需要启动p3线程对象
Print p1=new Print(p2);//num=1,执行完成后需要启动p2线程对象
p1.start();//只需要启动p1,就能启动所有连续需要启动的进程,
}
}
class Print extends Thread{
int num;
static int id;
Thread[] ts;
public Print(Thread ... ts ){//可变参实现不定数量传参
this.num=5-(id++);//num的值为5、4、3、2、1
this.ts=ts;
}
@Override
public void run(){
for(int i=0;i<100;i++){
System.out.println("我是线程"+num+"号");
}
if(ts!=null){//需要判断ts集合中是否有进程,有则让他启动
for(Thread t:ts){
t.start();
}
}
}
}
CountDownLatch用法详解参考链接
感谢您的浏览与点赞,让我们一起快乐学java!