线程:轻量级的进程---程序内部的顺序控制流
同一类线程共享代码和数据空间
多线程:同一个进程中同时运行多个线程用来完成不同工作
多个线程交替占用cpu资源,并非并行执行
线程每次执行的时长由分配的cpu时间片长度决定
实现线程的俩种方法:
1.继承Thread类 编写简单,可直接操作线程
具体操作:重写run()方法
创建线程对象,调用start方法启动线程
public class Thread_1 extends Thread{ //继承Thread类
@Override
public void run(){ //重写run方法
for(int i=1;i<=20;i++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i+".你好,来自线程"+getName());
}
}
}
public static void main(String[] args) {
Thread_1 t1=new Thread_1(); //创建线程对象
Thread_1 t2=new Thread_1();
t1.start(); //执行
t2.start();
}
}
2.实现runable接口 避免单继承局限性,便于共享资源
具体操作:重写run()方法
创建线程对象
调用start方法启动线程
public class Thread_2 implements Runnable{ //实现接口
@Override
public void run(){ //重写run方法
try{
System.out.println("运行");
Thread.sleep(500);
System.out.println("休眠");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("中断");
}
}
public static void main(String[] args) {
Thread t=new Thread(new Thread_2()); //创建对象
System.out.println("新建线程");
t.start(); //执行
try{
Thread.sleep(500); //休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程就绪");
}
}
线程对象调用start方法和调用run方法的区别:前者是调用实例方法,只有主线程执行,只有一条执行路径,后者是启动线程。
线程状态:创建、就绪、运行、阻塞、终止状态
创建状态:在程序中用构造方法创建了一个线程对象后,新的线程对象就处于创建状态,此时,它已经获取了相应的资源,但还没有处于可运行状态,这时可以通过Thread类的方法来设置线程对象的属性,如设置线程名(setName())、设置线程优先级(setPriority())等
就绪状态:线程创建之后,就可以通过调用start()方法启动线程,即进入就绪状态。此时,线程将进入线程队列排队,等待CPU资源,这表明它已经具备了运行条件,在未获得CPU资源时,仍不能真正执行
运行状态:当就绪状态的线程获得CPU资源时,即可转入运行状态,执行的run()方法。对于只有一个CPU的机器而言,任何时刻只能有一个处于运行状态的线程占用CPU,即获得CPU资源
阻塞状态:一个正在运行的线程因某种原因不能继承运行时,进入阻塞状态。阻塞状态是一种“不可运行”的状态,而处于这种状态的线程在得到一个特定的事件之后会转回可运行状态
死亡状态:一个线程的run()方法运行完毕,线程则进入死亡状态。处于死亡状态的线程不具有继承运行的能力
可能使线程暂停执行的条件:
线程优先级比较低,因此它不能获得CPU资源。
使用sleep()方法使线程休眠。
通过调用wait()方法,使线程等待。
通过调用yield()方法,线程显式出让CPU控制权。
线程由于等待一个文件I/O事件被阻塞。
线程调度的方法:
优先级高的线程获得cpu资源的概率大(优先级高的不一定先执行),默认优先级为5,优先级范围为1-10
public class MyThread implements Runnable{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "\t正在运行:"+i);
}
}
public static void main(String[] args) {
Thread t1=new Thread(new MyThread(),"线程A"); //通过构造方法指定线程名
Thread t2=new Thread(new MyThread(),"线程B");
//设置线程的优先级(对比设置前后的结果)
t1.setPriority(Thread.MAX_PRIORITY); //给当前线程设置优先级最高
t2.setPriority(Thread.MIN_PRIORITY); //给当前线程设置优先级最低
System.out.println("线程A的优先级"+t1.getPriority()); //getPriority();查看当前线程的优先级
System.out.println("线程B的优先级"+t2.getPriority());
t2.start();
t1.start();
运行结果:
线程休眠:让线程暂时休眠,线程进入阻塞状态,休眠结束后线程进入可运行状态
public class ThreadSleep {
public static void main(String[] args) {
System.out.println("wait");
Wait.bySec(5); // 让主线程等待5秒种再执行
System.out.println("start");
}
static class Wait{
static void bySec(long s){
for(int i=0;i<s;i++){
System.out.println(i+1+"s");
try{
Thread.sleep(1000); // 睡眠1秒
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
运行结果:主线程等待5s刚好执行完Wait方法,每秒打印一次,共五次然后执行主线程打印出start
线程阻塞--join():阻塞主线程,让子线程强制执行
public class Thread_3 implements Runnable{
@Override
public void run(){
for(int i=0;i<5;i++){
try{
Thread.sleep(100); //线程休眠0.1s
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t运行\t"+i);
}
}
}
public static void main(String[] args) {
Thread temp=new Thread(new Thread_3()); //创建子线程对象
temp.start(); //线程执行
for(int i=0;i<10;i++){
if(i==1){
try{
temp.join(); //阻塞主线程,子线程强制执行
}catch(InterruptedException e){
e.printStackTrace();
}
}try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\trun\t"+i);
}
}
}
运行结果:主线程main在执行到1时开始阻塞,子线程Thread-0执行完继续执行主线程
线程礼让--yield():暂停当前线程,允许其他具有相同优先级的线程获得运行机会。---只提供一种可能,不一定会礼让
礼让的线程为就绪状态,并不是阻塞状态
public class Thread_4 implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"\trun\t"+i);
if(i==3){
System.out.println("线程礼让");
Thread.yield(); //线程礼让
}
}
}
}
public class ThreadYield {
public static void main(String[] args) {
Thread a=new Thread(new Thread_4(),"线程A"); //创建线程对象a
Thread b=new Thread(new Thread_4(),"线程B"); //创建线程对象b
a.start();
b.start();
}
}
运行结果:线程A B在执行到2时都会执行礼让
使用多线程模拟多人爬山:每个线程代表一个人,每爬完100米显示信息,爬到终点给出相应提示
public class ClimbThread implements Runnable {
private int num;
private long time;
public ClimbThread(int time,int kilometer){
this.time=time;
this.num=kilometer*10;
}
@Override
public void run() {
while(num>0){
try {
Thread.sleep(this.time); //休眠达到速度的差异
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"爬完100米");
num--; //控制爬山的距离控制线程的进行
}
System.out.println(Thread.currentThread().getName()+"爬完了");
}
}
public static void main(String[] args) {
//ClimbThread
ClimbThread m=new ClimbThread(1000,1);
ClimbThread t=new ClimbThread(500,1);
Thread s=new Thread(m,"老年人");
Thread v=new Thread(t,"年轻人");
s.start();
v.start();
}
}
运行结果:
设置线程优先级:线程对象.setPriority(线程优先级)
使用线程实现医院叫号:
设置普通号为主线程,设置特需号为子线程;
使用线程休眠实现看病时间
设置特需号子线程的优先级为10实现概率高于普通号
使用线程阻塞实现特需号在普通号达到十号后先看特需号
public class ThreadLine implements Runnable {
@Override
public void run() {
for(int i=1;i<=10;i++){
System.out.println("特需号:"+i+"号病人在看病");
try{
Thread.sleep(2000); //设置特需号看病时长为普通号2倍
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread t=new Thread(new ThreadLine());
t.setPriority(10); //设置线程优先级为10
t.start();
for(int i=1;i<=50;i++){
System.out.println("普通号:"+i+"号病人在看病");
try{
Thread.sleep(1000); //设置普通号的看病时长
} catch (InterruptedException e) {
e.printStackTrace();
}if(i==3){
try{
t.join(); //子线程强制执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
线程同步:线程交互进行,一个执行完进行下一个
线程异步:线程同时进行,交叉执行
synchronized--线程锁:为当前线程声明一个锁
线程同步--用锁所著俩条线程,锁的对象要相同,相同指--地址相同
synchronized()内可以放空字符串,也可以放两个相同的值
同步代码块:重写run()方法后声明锁,其他代码都在锁内
网络购票:
package com.ls.use;
public class Site implements Runnable{
private int count=10; //记录剩余票数
private int num = 0; //记录买到第几张票
public void run(){
while(true){
//同步代码块
synchronized ("") {
//没有余票时,跳出循环
if (count <= 0) {
break;
}
//第一步:修改数据
num++;
count--;
try {
Thread.sleep(500); //模拟网络延时
} catch (InterruptedException e) {
e.printStackTrace();
}
//第二步:显示信息
System.out.println(Thread.currentThread().getName() + "抢到第" + num + "张票,剩余" + count + "张票!");
}
}
}
}
package com.ls.use;
public class Test {
public static void main(String[] args) {
Site site = new Site();
Thread person1= new Thread(site,"A");
Thread person2= new Thread(site,"B");
Thread person3= new Thread(site,"C");
System.out.println("********开始抢票********");
person1.start();
person2.start();
person3.start();
}
}
运行结果:
设置线程锁可以实现多条线程同步进行