【无标题】

本文详细讲解了线程的基本概念、创建与启动、优先级调整、守护线程、生命周期管理,以及线程安全与同步方法,包括synchronized关键字、Lock接口的应用。通过实例演示了如何避免数据一致性问题,确保线程间的协作有效。
摘要由CSDN通过智能技术生成

多线程

电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。
 1.线程中常用的方法
  1.static Thread currentThread()得到当前正在运行的线程对象
  2.void start() 启动线程
  3.String getName()返回该线程的名称。
   1.当没有设置线程名称的时候,系统会赋予线程一个默认的名称“Thread-0,Thread-1…”
   2.主线程【主方法的执行线程】的名称默认是“main”
  4.void setName(String name)设置线程名称
例如:

package com.wangxing.zyb1;
public class MyThread1 implements Runnable{
	@Override
	public void run() {
		for(int i=1;i<=20;i++) {
			//static Thread currentThread()得到当前正在运行的线程对象
			Thread  th=new Thread();
			String name=th.getName();
			System.out.println(name+",i=="+i);
		}
	}
}
package com.wangxing.zyb1;
public class Main {
	public static void main(String[] args) {
	//主方法执行线程的名称
	String name=Thread.currentThread().getName();
	System.out.println("主方法执行的名称="+name);
	//创建目标对象
	MyThread1 my=new MyThread1();
	//创建线程对象
	Thread th1 = new Thread(my);
	Thread th2 = new Thread(my);
	th1.setName(name);
	th2.setName(name);
	//void start() 启动线程
	th1.start();
	th2.start();
	}
}

在这里插入图片描述
线程的优先级—就是线程的执行先后,默认情况下所有线程的优先级都是一样,都是5。
我们可以通过void setPriority(int newPriority) 更改线程的优先级。
 1.线程的优先级有10个级别,分别使用整数1~10来表示。数字越大优先级越高。
 2.为了方便操作,java将10个级别有规定成3个级别,分别是最低的优先级,中等优先级,最高的优先级,并且将这3个级别封装成了静态常量 static int MAX_PRIORITY 线程可以具有的最高优先级。
  static int MIN_PRIORITY线程可以具有的最低优先级。1
  static int NORM_PRIORITY分配给线程的默认优先级。5
  int getPriority() 返回线程的优先级。
 3.设置线程的优先级的时候,数字越大优先级越高,数字越小优先级越低。优先级越高并代表就一定会优先执行,只是被优先执行的几率增大,因此不要试图通过控制线程的优先级,来保证某一个线程,总是第一个执行。
例如:

package com.wangxing.zyb2;

public class MyThread1 implements Runnable{

	@Override
	public void run() {
		for(int i=1;i<=20;i++) {
			//static Thread currentThread()得到当前正在运行的线程对象
			Thread  th=new Thread();
			String name=th.getName();
			System.out.println(name+",i=="+i);
		}
	}
}
package com.wangxing.zyb2;
public class Main {
	public static void main(String[] args) {
	//主方法执行线程的名称
	String name=Thread.currentThread().getName();
	//主方法执行线程的优先级
	//int getPriority() 返回线程的优先级。
	int mainpri=Thread.currentThread().getPriority();
	System.out.println("主方法执行线程的名称=="+name+",优先级=="+mainpri);
	//创建目标对象
	MyThread1 my=new MyThread1();
	//创建线程对象
	Thread th1 = new Thread(my);
	Thread th2 = new Thread(my);
	//void setName(String name)设置线程名称
	th1.setName("线程1");
	th2.setName("线程2");
	//void	setPriority(int newPriority) 更改线程的优先级。
	//线程的优先级有10个级别,分别使用整数1~10来表示。数字越大优先级越高。
	//为了方便操作,java将10个级别有规定成3个级别用静态常量表示
	th1.setPriority(10);
	th2.setPriority(Thread.MIN_PRIORITY);
	//int	getPriority() 返回线程的优先级。
	int th1pri=th1.getPriority();
	int th2pri=th2.getPriority();
	System.out.println("线程1的优先级=="+th1pri);
	System.out.println("线程2的优先级=="+th2pri);
	//void start() 启动线程
	th1.start();
	th2.start();
	}
}

守护线程的相关操作方法
 用户线程----通常情况之下我们所创建的线程都是普通线程,非守护线程,也叫用户线程。
 守护线程----也叫精灵线程,当所有用户线程都执行完毕以后,自动结束运行的线程就是守护线程.[共死]
 1.boolean isDaemon() 测试该线程是否为守护线程。
 2.void setDaemon(boolean on) 将该线程标记为守护线程用户线程。
 特征:当所有用户线程都执行完毕以后,无论守护线程能否可以继续运行,都要立刻停止运行。
例如:

package com.wangxing.zyb3;
public class MyThread1 implements Runnable{
	@Override
	public void run() {
		for(int i=1;i<=20;i++) {
			//static Thread currentThread()得到当前正在运行的线程对象
			Thread  th=new Thread();
			String name=th.getName();
			System.out.println(name+",i=="+i);
		}
	}
}
package com.wangxing.zyb3;
public class MyThread2 implements Runnable{
	@Override
	public void run() {
		while(true) {
			Thread  th=Thread.currentThread();
			String name=th.getName();
			System.out.println(name);
		}
	}
}
package com.wangxing.zyb3;
public class Main {
	public static void main(String[] args) {
	//创建目标对象
	MyThread1 my1=new MyThread1();
	MyThread1 my2=new MyThread1();
	//创建线程对象
	Thread th1 = new Thread(my1,"用户线程");
	Thread th11 = new Thread(my1,"用户线程");
	Thread th2 = new Thread(my2,"用户线程");
	//void	setDaemon(boolean on) 将该线程标记为守护线程用户线程。
	th2.setDaemon(true);
	//void setName(String name)设置线程名称
	th1.setName("线程1");
	th2.setName("线程2");
	//boolean	isDaemon() 测试该线程是否为守护线程。
	System.out.println("th1==="+th1.isDaemon());
	System.out.println("th2==="+th2.isDaemon());
	//启动线程
	th1.start();
	th11.start();
	th2.start();
	}
}

static void sleep(long millis) 设置线程休眠【暂停】指定的时间【毫秒】
例如:闹钟程序

package com.wangxing.zyb4;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
public class NaoZhong{
	public static void main(String[] args)throws Exception {
		System.out.println("设置一个闹钟");
		BufferedReader input=new BufferedReader(new InputStreamReader(System.in));
		String nzsj=input.readLine();
		SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
		boolean flag=true;
		while(flag) {
			String dqsj=sdf.format(new Date());
			if(dqsj.equals(nzsj)) {
				flag=false;
			}
			System.out.println(dqsj);
			Thread.sleep(1000);
		}
		System.out.println("闹钟响起");
	}
}

在这里插入图片描述
void interrupt() 中断线程休眠【暂停】。会进入异常【InterruptedException】。
例如:

package com.wangxing.zyb4;
public class MyThread1 implements Runnable{
	@Override
	public void run() {
		//得到当前线程的名称
		String name=Thread.currentThread().getName();
		System.out.println(name+"开始听讲");
		//设置static void	sleep(long millis) 设置线程休眠【暂停】指定的时间【毫秒】
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(name+"打瞌睡...");
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			System.out.println(name+"被老师叫醒");
			System.out.println(name+"继续开始听课");
			try {
				Thread.sleep(5000);
			}catch(InterruptedException e1) {
				e1.printStackTrace();
			}
			System.out.println("下课了");
		}
	}
}
package com.wangxing.zyb4;
public class Main {
	public static void main(String[] args) {
		System.out.println("铃铃铃!上课了");
		MyThread1 my=new MyThread1();
		Thread th=new Thread(my);
		th.getName();
		th.start();
		System.out.println("老师开始上课");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}
		System.out.println("老师发现童鞋睡着了");
		System.out.println("老师过去叫醒了同学");
		th.interrupt();
	}
}

void join(long millis)【强制线程执行】等待该线程终止的时间最长为 millis 毫秒。

例如:

package com.wangxing.zyb5;
public class MyThread1 implements Runnable{
	@Override
	public void run() {
		for(int i=1;i<=20;i++) {
			//得到当前线程的名称
			String name=Thread.currentThread().getName();
			System.out.println(name+",i=="+i);
		}
	}
}
package com.wangxing.zyb5;
public class Main {
	public static void main(String[] args) {
		// 创建目标对象
		MyThread1 my = new MyThread1();
		// 创建线程对象
		Thread th1 = new Thread(my);
		// 设置名字
		th1.setName("线程1");
		th1.start();
		for (int i = 1; i <= 20; i++) {
			// 得到当前线程的名称
			String name = Thread.currentThread().getName();
			System.out.println(name + ",i==" + i);
			if (i == 10) {
				// 强制线程1执行
				// void join(【long millis】)【强制线程执行】等待该线程终止的时间最长为 millis 毫秒。
				try {
					th1.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

2.线程的生命周期
1、线程的生命周期就是线程从一开始创建,到run方法执行完毕以后的状态变化。[状态之间的切换]
2、线程的生命周期几种状态【1、新建状态 2、就绪状态 3、运行状态 4.阻塞状态 5.死亡状态】
参考图片1.png
在这里插入图片描述
线程的生命周期描述
 1.新建状态:通过new的方式创建出线程对象,此时线程就进入到创建状态【新建状态】。
  新建状态的线程是不能运行。
  新建状态的线程调用start方法,进入就绪状态
2.就绪状态:线程具备运行能力,只差操作系统【CPU】分配给他运行时间片
  得到操作系统【CPU】分配给他运行时间片,此时开始执行run方法,进入运行状态。
 3.运行状态:线程运行run方法。
  回到就绪状态:
 1.操作系统【CPU】分配给他运行时间片使用完毕,回到就绪状态。
  进入阻塞状态
  1.运行状态的线程执行了sleep方法,进入阻塞状态。
  2.运行状态的线程执行了wait方法,进入阻塞状态。
  3.运行状态的线程执行输入/输出动作,进入阻塞状态。
  …
  进入死亡状态
  1.运行状态的线程run方法执行完毕,进入死亡状态。
  2.运行状态的线程调用stop()/destroy() ,进入死亡状态。
 4.阻塞状态:线程暂停运行
  回到运行状态
  阻塞状态中的线程,结束了造成阻塞的原因,此时线程进入就绪状态,得到操作系统【CPU】分配给他运行时间片就可以进入运行状态。
  运行状态进入阻塞状态原因:
  1.运行状态的线程执行了sleep方法,进入阻塞状态,休眠时间结束/interrupt,进入就绪状态
  2.运行状态的线程执行了wait方法,进入阻塞状态,调用notify/notifyAll,进入就绪状态
  3.运行状态的线程执行输入/输出动作,进入阻塞状态,输入/输出结束,进入就绪状态
 5.死亡状态:线程运行结束,释放运行资源。
  死亡状态的线程是不能运行,除非再一次使用strat方法重新启动运行。
 3.线程安全
  卖票实例:

package com.wangxing.zyb6;
public class MyThread1 implements Runnable{
	private int piao=20;
	@Override
	public void run() {
		//得到当前线程的名称
		String name=Thread.currentThread().getName();
		//持续买票
		boolean flag=true;
		while(flag) {
			//判断有没有可卖的票
			if(piao>0) {
				//收钱--找钱--打印票
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else {
				flag=false;
			}
		}
	}
}
package com.wangxing.zyb6;

public class Main {
	public static void main(String[] args) {
		// 创建目标对象
		MyThread1 my = new MyThread1();
		// 创建线程对象
		Thread th1 = new Thread(my);
		Thread th2 = new Thread(my);
		Thread th3 = new Thread(my);
		// 设置名字
		th1.setName("线程1");
		th2.setName("线程2");
		th3.setName("线程3");
		th2.setPriority(10);
		th1.start();
		th2.start();
		th3.start();
	}
}

运行结果:
在这里插入图片描述
  分析结果:当窗口1卖最后一张票的时候,在收钱打印票的时候,还没有来得及对票数进行减1之前,线程就切换给了窗口3,窗口3认为还有一张票,窗口3就收钱收钱打印票的时候,还没有来得及对票数进行减1之前,线程有切换给了窗口1,窗口1就对票数进减1,完成以后线程切换给窗口3,窗口3对对票数进行减1此时就得到-1这个值。
  经过上面运行程序的分析,我得到的结果是:当多条线程,同时访问同一个资源的时候,会产生数据不一致的错误情况。为了解决这种数据不一致的错误情况,我们才学习线程同步。
 什么是线程同步/线程安全?
  线程同步也叫线程安全,当多条线程,同时访问同一个资源的时候,每一次只能由多条线程中的其中一条访问公共资源,当这一条线程访问公共资源的时候,其他的线程都处于等待状态,不能访问公共资源,当这一条线程访问完了公共资源以后,其他线程中的一条线程才能访问资源,剩下的线程继续等待,等待当前线程访问结束,实现这个过程就是线程同步。【排队访问资源】
 线程同步/线程安全的实现方式有几种,分别是什么,有什么区别?
  1.Synchronized关键字 【同步代码块/同步方法】
  1.同步代码块
 格式:synchronized(同步对象){
 
   }
例如:

package com.wangxing.zyb6;
public class MyThread2 implements Runnable {
	private int piao = 20;
	@Override
	public void run() {
		// 得到当前线程的名称
		String name = Thread.currentThread().getName();
		// 持续买票
		boolean flag = true;
		while (flag) {
			// 同步代码块
			synchronized (this) {
				// 判断有没有可卖的票
				if (piao > 0) {
					// 收钱--找钱--打印票
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(name + ",卖出1张票,还剩" + (--piao) + "张");
				} else {
					flag = false;
				}
			}
		}
	}
}
package com.wangxing.zyb6;
public class Main {
	public static void main(String[] args) {
		// 创建目标对象
		MyThread2 my = new MyThread2();
		// 创建线程对象
		Thread th1 = new Thread(my);
		Thread th2 = new Thread(my);
		Thread th3 = new Thread(my);
		// 设置名字
		th1.setName("线程1");
		th2.setName("线程2");
		th3.setName("线程3");
		th2.setPriority(10);
		th1.start();
		th2.start();
		th3.start();
	}
}

同步代码块虽然可以实现买票的效果,但是它在使用的时候,需要设置一个同步对象,由于我们很多时候都不知道这个同步对象应该是谁,容易写错,造成死锁的情况。正是应为这个缺点,我们很少使用同步代码块来实现线程同步。
 2.同步方法
  同步方法的定义格式: 访问限制修饰符 synchronized 方法返回值类型 方法名称(){

		            }

例如:

package com.wangxing.zyb6;
public class MyThread3 implements Runnable {
	private int piao = 20;
	//持续买票
	boolean flag = true;
	@Override
	public void run() {
		// 得到当前线程的名称
		String name = Thread.currentThread().getName();
		while (flag) {
			sellpiao(name);
		}
	}
	private synchronized void sellpiao(String name) {
		//判断有没有可卖的票
		if(piao>0) {
			//收钱--找钱--打印票
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
		}else {
			flag=false;
		}
	}
}
package com.wangxing.zyb6;
public class Main {
	public static void main(String[] args) {
		// 创建目标对象
	 MyThread3 my = new MyThread3();
		// 创建线程对象
		Thread th1 = new Thread(my);
		Thread th2 = new Thread(my);
		Thread th3 = new Thread(my);
		// 设置名字
		th1.setName("线程1");
		th2.setName("线程2");
		th3.setName("线程3");
		th2.setPriority(10);
		th1.start();
		th2.start();
		th3.start();
	}
}

2.通过Lock接口
 public interface Lock
 常用的接口方法
 void lock() 获得锁。
 void unlock() 释放锁。
  由于上面的锁方法是Lock接口,我们要使用就得先创建出Lock接口对象,由于Lock是个接口不能new ,我们就得使用它的子类来创建对象。Lock接口得子类ReentrantLock
例如:

package com.wangxing.zyb6;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread4 implements Runnable {
	private int piao = 20;
	//定义Lock对象
	private Lock Mylock=new ReentrantLock();
	@Override
	public void run() {
		// 得到当前线程的名称
		String name = Thread.currentThread().getName();
		持续买票
		boolean flag=true;
		while (flag) {
			//void lock() 获得锁。 
			Mylock.lock();
			//判断有没有可卖的票
			if(piao>0) {
				//收钱--找钱--打印票
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else {
				flag=false;
			}
			//void	unlock() 释放锁。
			Mylock.unlock();
		}
	}
}
package com.wangxing.zyb6;
public class Main {
	public static void main(String[] args) {
		// 创建目标对象
		MyThread4 my = new MyThread4();
		// 创建线程对象
		Thread th1 = new Thread(my);
		Thread th2 = new Thread(my);
		Thread th3 = new Thread(my);
		// 设置名字
		th1.setName("线程1");
		th2.setName("线程2");
		th3.setName("线程3");
		th2.setPriority(10);
		th1.start();
		th2.start();
		th3.start();
	}
}

Synchronized关键字与Lock接口的区别?
synchronized:
  1.synchronized关键字
  2.自动锁定资源,不灵活
  3.异常时会自动释放锁
  4.不能中断锁,必须等待线程执行完成释放锁。
 Lock:
  1.Lock接口
  2.手动锁定资源,灵活
  3.异常时不会自动释放锁,所以需要在finally中实现释放锁
  4.可以中断锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值