哈尔滨网站建设哪儿好薇广告公司业务推广
一 名词解析
1 线程 : 控制指定APP(进程)执行的最基本单元(最小单位)
2 进程 : 硬件设备上的每一个应用程序
3 单线程 : 一个进程中只有一个线程执行,实际上基本没有这种情况
4 多线程 : 一个进程中至少有两个或两个以上的线程在执行
二 创建方式
1 共有三种:Thread类. Runnable接口,Callable接口
2 多线程操作:多个线程在同时一时刻操作共同资源
3 继承Thread类,重写run方法(编写多线程操作的代码),通过Thread类或Thread子类对象调用start方法开启多线程,并调用指定的run方法执行多线程操作,每次执行的结果都不一致
①Thread类:java中所有有关线程操作的类
// 子线程
public class SonThread extends Thread{public void run(){for(int i=0;i<20;i++){System.err.println("子线程::"+i);}}
}// 测试
public class ThreadTest {public static void main(String[] args) {SonThread st=new SonThread();for (int i = 0; i < 20; i++) {System.out.println("main::"+i);}// 调用重写的run方法,根据调用顺序执行线程操作(单线程按顺序执行)//st.run();// 实现多线程操作,通过Thread类对象调用start方法实现,run方法执行st.start();for (int i = 0; i < 20; i++) {System.err.println("main::"+i);}}
}
② 实现Runnable接口,重写run方法,需调用Thread类中的start方法开启多线程,并执行指定run方法,避免java中单继承的局限性,和继承带来的类与类之间的耦合度的增加
// 实现类
public class Son implements Runnable{@Overridepublic void run() {for (int i = 0; i < 500; i++) {System.err.println("Son::"+i);}}
}// 测试类
public class SonTest {public static void main(String[] args) {// 实例化实现Runable的对象Son s=new Son();// 与Thread类建立联系Thread t=new Thread(s);t.start();for (int i = 0; i < 500; i++) {System.out.println("main::"+i);}}
}
③ 实现Callable接口可返回结果并且可能抛出异常的任务。可以获得任务执行返回值;通过与Future的结合,可以实现利用Future来跟踪异步计算的结果。
// 实现callable接口
public class TestCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int i = 0;for (; i < 50; i++) {System.err.println("call::"+i);}return i;}
}// 使用
public class Play {public static void main(String[] args) {TestCallable tc = new TestCallable();FutureTask<Integer> ft = new FutureTask<>(tc);for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);if (i == 20) {new Thread(ft, "有返回值的线程").start();}}try {System.out.println("子线程的返回值:" + ft.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}
4 通过上述多线程操作,每次执行后,效果不一致,是因为多个线程同时操作相同资源,每个线程都会去"抢占"CPU的执行权,谁抢到,谁执行.实际上,是CPU在已开启的多个线程之间进行快速的切换,也称之为多线程执行的特点(随机性)
5 可提高指定线程的执行权限,java中为每个线程设置初始默认"等级",为5,等级取值范围[1,10],通过Thread类提供的指定的set方法去设置等级,即优先级;但是并不是优先级越大越先执行,而优先级的大小只是提高被执行完的可能
三 Thread类常见API
1 创建Thread类对象方法
public Thread()
2 将指定的Runnable接口的实现类对象,通过Thread构造器与Thread类建立联系
public Thread(Runnable r)
3 编写多线程操作代码方法
public void run()
4 开启多线程操作,并调用指定的run方法执行多线程
public void start()
5 获取当前线程的名字
public class SonThread extends Thread{public void run(){for(int i=0;i<50;i++){System.err.println(getName()+"::"+i);}}
}
6 在非Thread类子类中,获取当前正在执行线程对象
public class SonTest {public static void main(String[] args) {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread()+"::"+i);}//Thread[main,5,main]::40}//格式为:线程名 优先级 线程的执行位置
}
7 让指定的线程睡眠指定的时间(时间的单位是毫秒数)
// public static void sleep(long millis)
public class SonThread extends Thread{public void run(){for(int i=0;i<50;i++){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.err.println(getName()+"::"+i);}}
}
8 给线程设置线程名
public class SonThread extends Thread{public void run(){setName("我是线程");for(int i=0;i<50;i++){System.err.println(getName()+"::"+i);}}
}// 或者
public class SonTest {public static void main(String[] args) {Thread.currentThread().setName("我是线程");for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread()+"::"+i);}}
}
9 设置和获取线程的优先级,优先级的取值范围[1,10]
public int getPriority()public class SonTest {public static void main(String[] args) {SonThread st=new SonThread();st.start();// 设置st.setPriority(10);for (int i = 0; i < 50; i++) {System.err.println(Thread.currentThread()+"::"+i);}// 获取System.out.println(st.getPriority());}
}
四 多线程的状态
状态 | 权限 |
---|---|
创建状态 | 生存权 |
运行状态 | 既有生存权又有执行权 |
消亡状态 | 既没有生存权也没有执行权 |
临时(阻塞)状态 | 只有生存权没有执行权 |
1 创建状态下的生存权和临时状态的生存权的区别
在于多线程操作是否被开启;创建状态下,没有进行多线程操作;临时状态下的生存权在开启多线程之后,没有获取到执行权
2 处于临时状态下的线程存储位置是在由jvm虚拟机提供的线程池,进出的原则,先进先出(类似于吸管,也称之为队列式数据存储
3 等待唤醒机制的等待和唤醒方法定义在Object类中,而不是定义在Thread类中
由于实际开发中,并不是所有的类都要继承Thread类,但是只要是java的类就是多线程操作,等待和唤醒的方法是共性的,需要定义在所有类的父类中,因此就把它定义在了Object类中,实际大家更喜欢用sleep来完成多线程操作:到时候自动苏醒
五 案例-火车站窗口售票
// 测试类
public class Test {public static void main(String[] args) {Window w=new Window();//通过Thread构造器建立连接// 多线程操作时,多个线程可以同时操作共同资源,当一个线程操作时,其他的线程也有操作的可能Thread w1=new Thread(w);Thread w2=new Thread(w);Thread w3=new Thread(w);w1.start();w2.start();w3.start();}
}// 同步代码块版Window
public class Window implements Runnable{private int tickets=100;@Overridepublic void run() {// 火车站窗口24小时开放while (true){// 添加锁,防止超卖问题(3种)// Object obj=new Object();// this --本类对象锁// Window.class --反射机制锁,通过字节码文件获取synchronized (Window.class){//同步代码块try {Thread.sleep(10);} catch (Exception e) {e.printStackTrace();}if(tickets > 0){System.err.println(Thread.currentThread().getName()+"::"+tickets--);}}}}
} // 同步函数版Window
public class Window implements Runnable{private int tickets=100;@Overridepublic void run() {// 火车站窗口24小时开放while (true){fun();}}// 同步函数private synchronized void fun() {if(tickets > 0){try {Thread.sleep(10);} catch (Exception e) {e.printStackTrace();}System.err.println(Thread.currentThread().getName()+"::"+tickets--);}}
}
同步函数和同步代码块的区别
① 同步代码块有3把锁:Object对象锁 this本类对象锁 反射机制锁
② 同步函数有1把锁:this本类对象锁
③ 同步函数只能锁定当前对象(即this),而同步代码块可以锁定任意对象。
④ 同步函数的锁是在方法调用时自动加上的,而同步代码块需要手动指定锁对象。
⑤ 同步函数的锁是在方法调用结束时自动释放的,而同步代码块需要手动释放锁。