Java中的多线程2

本文详细介绍了Java线程中的常用方法,包括启动线程、获取和设置线程名称、优先级以及守护线程的操作。此外,还讨论了线程的生命周期,包括新建、就绪、运行、阻塞和死亡状态。线程安全问题通过买票实例展示,解释了同步的重要性,并对比了`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.test1;

public class MyThread implements Runnable{

	@Override
	public void run() {
		for(int i=1;i<=50;i++){
			//static Thread currentThread()得到当前正在运行的线程对象
			Thread th=Thread.currentThread();
			String name=th.getName();
			System.out.println(name+",i=="+i);
		}
		
	}
	
}

package com.test1;

public class ThreadMain {

	public static void main(String[] args) {
		//主方法执行线程的名称
		String name=Thread.currentThread().getName();
		System.out.println("主方法执行线程的名称=="+name);
		//创建目标对象
		MyThread my=new MyThread();
		//创建线程对象
		Thread th=new Thread(my);
		Thread th2=new Thread(my);
		//void  setName(String name)设置线程名称
		th.setName("线程1");
		th2.setName("线程2");
		//void start()启动线程
		th.start();
		th2.start();

	}

}

 

        线程的优先级---就是线程的执行先后。

        默认情况下所有线程的优先级都是一样,都是5

        我们可以通过

        void         setPriority(int newProprity)更改线程的优先级。

        1.线程的优先级有10个级别,分别使用整数1~10来表示。数字越大优先级越高。

        2.为了方便操作,java将10个级别有规定成3个级别,分别是低的优先级,中等优先级,最高好优先级,并且将这个3个级别封装成了静态常量

        static int MAX_PRIORITY线程可以具有最高的优先级 10

        static int MIN_PRIORITY线程可以具有最低的优先级 1

        static int NORM_PRIORITY分配给线程的默认优先级 5

        int  getPriority()返回线程优先级

        3.设置线程的优先级的时候,数字越大优先级越高,数字越小优先级越低。优先级越高并不代表就一定会有优先执行,只是被优先执行的几率增大,因此不要试图通过控制线程的优先级,来保证某一个线程,总是第一个执行。

例如:

package com.test2;

public class MyThread implements Runnable{

	@Override
	public void run() {
		for(int i=1;i<=5;i++){
			//得到当前正在运行的线程对象的名称
			String name=Thread.currentThread().getName();
			System.out.println(name+",i=="+i);
		}
		
	}
	
}
package com.test2;

public class ThreadMain {

	public static void main(String[] args) {
		//主方法执行线程的名称
		String name=Thread.currentThread().getName();
		//主方法的线程优先级
		//int getPriority()返回线程的优先级
		int mainpri=Thread.currentThread().getPriority();
		System.out.println("主方法执行线程的名称=="+name+",优先级=="+mainpri);
		//创建目标对象
		MyThread my=new MyThread();
		//创建线程对象
		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来表示,数字越大优先级越高
		th1.setPriority(10);
		//为了方便操作,java将10个级别有规定成3个级别用静态常量表示
		th2.setPriority(Thread.MIN_PRIORITY);
		//int getPriority()返回线程的优先级
		int th1pri=th1.getPriority();
		int th2pri=th2.getPriority();
		System.out.println("线程1的优先级=="+th1);
		System.out.println("线程2的优先级=="+th2);
		//void start()启动线程
		th1.start();
		th2.start();
	}

}

 

守护线程的相关操作

        用户线程---通常情况下我们所创建的线程都是普通线程,非守护线程,也叫用户线程

        守护线程---也叫精灵线程,当所有用户线程执行完毕以后,自动结束运行的线程就是守护线程[共死] 

        1.booleanisDaemon()测试该线程是否为守护线程。

        2.void         setDaemon(boolean on)就昂该线程标记为守护线程用户线程

        特征:当所有用户线程都执行完毕以后,无论守护线程能否可以继续运行,都要立刻停止运行

例如:

package com.test3;

public class MyThread implements Runnable{

	@Override
	public void run() {
		for(int i=1;i<=50;i++){
			//得到当前正在运行的线程对象的名称
			String name=Thread.currentThread().getName();
			System.out.println(name+",i=="+i);
		}
		
	}
	
}
package com.test3;

public class MyThread2 implements Runnable{

	@Override
	public void run() {
		while(true){
			String name=Thread.currentThread().getName();
			System.out.println(name);
		}
		
	}

}
package com.test3;

public class ThreadMain {

	public static void main(String[] args) {
		//创建目标对象
		MyThread my1=new MyThread();
		MyThread2 my2=new MyThread2();
		//创建线程对象
		Thread th1=new Thread(my1);
		Thread th11=new Thread(my1);
		Thread th2=new Thread(my2);
		//void setDaemon(boolean on)将该线程标记为守护线程用户线程
		th2.setDaemon(true);
		//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.test4;

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("设置一个闹钟");
		InputStreamReader pustream=new InputStreamReader(System.in);
		BufferedReader input=new BufferedReader(pustream);
		String naozhongshijian=input.readLine();
		SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
		boolean flag=true;
		while(flag){
			String dangqianshijian=sdf.format(new Date());
			if(dangqianshijian.equals(naozhongshijian)){
				flag=false;
			}
			System.out.println(dangqianshijian);
			Thread.sleep(1000);
		}
		System.out.println("闹钟响起");
	}
}

 void        interrupt()中断线程休眠【暂停】,会进入异常【InterruptedException】。

例如:

package com.test4;

public class MyThread 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.test4;

public class ThreadMain {

	public static void main(String[] args) {
		System.out.println("上课铃响啦");
		MyThread my=new MyThread();
		Thread th=new Thread(my);
		th.setName("玉藻前");
		th.start();
		
		System.out.println("老师开始上课");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("老师发现同学睡着了");
		System.out.println("老师走过去,踹了一脚!");
		//void interrupt()中断线程休眠【暂停】会进入异常【InterruptedException】
		th.interrupt();
	}

}

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

例如:

package com.test5;

public class MyThread implements Runnable{

	@Override
	public void run() {
		
		for(int i=1;i<=50;i++){
			//得到当前线程的名称
			String name=Thread.currentThread().getName();
			System.out.println(name+",i=="+i);
		}
		
	}

}
package com.test5;

public class ThreadMain {

	public static void main(String[] args) {
		//创建目标对象
		MyThread my=new MyThread();
		//创建线程对象
		Thread th=new Thread(my);
		//更改线程名称
		th.setName("线程1");
		//启动线程
		th.start();
		
		for(int i=1;i<=50;i++){
			//得到当前线程的名称
			String name=Thread.currentThread().getName();
			if(i==10){
				//强制线程1执行
				//void	join([long millis])【强制线程执行】等待该线程终止的时最长为millis
				try {
					th.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("main"+i);
			}
		}
	}

}

2.线程的生命周期

               1.线程的生命周期就是线程从一开始创建,到run方法执行完毕以后的状态变化。【线程之间的切换】

                2.线程的生命周期集中状态【1.新建状态        2.就绪状态        3.运行状态        4.阻塞状态        5.死亡状态】

        参考图片:

 

线程生命周期描述

        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.运行状态的线程hi行输入/输出动作,进入阻塞状态,输入/输出结束,进入就绪状态

        5.死亡状态:线程运行结束,释放余小宁资源。

                        死亡状态的线程是不能运行的,除非再一次使用strat方法重新启动运行

3.线程安全

        买票实例:

package com.test6;

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

public class ThreadMain {

	public static void main(String[] args) {
		//创建目标对象
		MyThread my=new MyThread();
		//创建线程对象
		Thread th1=new Thread(my);
		Thread th2=new Thread(my);
		Thread th3=new Thread(my);
		th1.setName("窗口1");
		th2.setName("窗口2");
		th3.setName("窗口3");
		th1.start();
		th2.start();
		th3.start();
	}

}

 

 

分析结果:当窗口1卖出最后一张票的时候,在收钱打印票的时候,还没有来得及对票数进行减1之前,线程就切换给了窗口3,窗口3认为还有一张票,窗口3就收钱找钱打印票的时候,还没有来得及对票数进行减1之前,线程又切换给了窗口1,窗口1就对票数1进行减1,完成以后线程切换给窗口3,窗口3对票数进行减1此时就得到-1这个值。

经过上面运行程序的分析,得到的结果是:当多条线程,同时访问同一个资源的时候,会产生数据不一致的错误情况。为了解决这种数据不一致的错误情况,我们才学习线程同步。

什么是线程同步/线程安全?

        线程同步也叫线程安全,当多条线程,同时访问同一个资源的时候,每一次只能由多线程中的其中一条访问公共资源,当这一条线程访问公共资源的时候,其他线程都处于等待状态,不能访问公共资源,当这一条线程访问完了公共资源以后,其他线程中的一条线程才能访问资源,剩下的线程继续等待,等待当前线程访问结束,实现这个过程就是线程同步。【排队访问资源】

线程同步/线程安全的实现方式有几种,分别是什么,有什么区别?

        1.Synchronized关键字【同步代码块/同步方法】

                1.同步代码块

                        格式:Synchronized(同步对象){                }

例如:

package com.test6;

public class MyThread2 implements Runnable{
	private int piao=5;
	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		//持续卖票
		boolean flag=true;
		while(flag){
            //同步代码块
			synchronized (this) {
			//判断有没有可卖的票
			if(piao>0){
				//收钱--找钱--打印票
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else{
				flag=false;
				}
			}
		}
		
	}
	
}

同步代码块虽然可以实现买票的效果,但是他在使用的时候,需要设置一个同步对象,由于我们很多时候都不知道这个同步对象是谁,容易写错,造成锁死的情况。正式因为这个缺点,我们很少使用同步代码块来实现线程同步。

        2.同步方法

                同步方法的定义格式:访问限制修饰符        synchronize        方法返回值类型         方法名称(){                }

例如:

package com.test6;

public class MyThread3 implements Runnable{
	private int piao=5;
	boolean flag=true;
	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		//持续卖票
		
		while(flag){
			sellpiao(name);
		}
	}
		//同步方法
		public synchronized void  sellpiao(String name){
			//判断有没有可卖的票
			if(piao>0){
			
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else{
				flag=false;
			}
		}
			
		
		
	
	
}

通过Lock接口

        public interface  Lock

        常用的接口方法

        void         lock()获得锁

        void         unlock()释放锁

由于上面的锁方法是Lock接口,我们要使用就得先创建出Lock接口对象,由于Lock这个接口不能new,我们就得使用它的子类来创建对象。

        Lock接口的子类ReentrantLock

例如:

package com.test6;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread4 implements Runnable{
	//定义Lock对象
	private Lock mylock=new ReentrantLock();
	private int piao=5;
	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		//持续卖票
		boolean flag=true;
		while(flag){
			//void lock()获得锁
			mylock.lock();
			//判断有没有可卖的票
			if(piao>0){
				//收钱--找钱--打印票
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else{
				flag=false;
			}
			//void unlock()释放锁
			mylock.unlock();
		}
		
	}
	
}

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、付费专栏及课程。

余额充值