Java多线程

首先要了解操作系统中进程与线程的区别;

进程:是程序的一次动态执行过程,它对应了代码的加载、执行至执行完毕的完整过程,这个过程也是进程本身从产生、发展至消亡的过程,每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位);

线程:是比进程更小的单位,进程在其执行过程中,可以产生多个线程,形成多执行线索,即每个线程也有它自身的产生、存在和消亡过程,也是一个动态的概念,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位);

线程与进程一样分为五个阶段:创建、就绪、运行、阻塞、终止

多进程是指系统可以同时运行多个任务。

多线程是指在同一程序中有多个顺序流在执行。

java中要想实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口。

一、继承Thread类

public class ThreadByExtends_1 {

	public static void main(String[] args) {//主线程
		SpeakHello sh = new SpeakHello();
		SpeakNihao sn = new SpeakNihao();
		sh.start();
		sn.start();
		for(int i = 1; i <= 5; i++) {
			System.out.println("大家好"+i+" ");
		}
	}

}

class SpeakHello extends Thread{
	public void run() {
		for(int i = 1; i <= 5; i++) {
			System.out.println("hello"+i+" ");
		}
	}
}

class SpeakNihao extends Thread{
	public void run() {
		for(int i = 1; i <= 5; i++) {
			System.out.println("你好"+i+" ");
		}
	}
}

运行结果:

大家好1 
hello1 
你好1 
hello2 
大家好2 
hello3 
你好2 
hello4 
大家好3 
hello5 
你好3 
你好4 
你好5 
大家好4 

大家好5 

第二次运行:

大家好1 
你好1 
hello1 
你好2 
大家好2 
你好3 
hello2 
你好4 
大家好3 
你好5 
大家好4 
大家好5 
hello3 
hello4 

hello5 

当执行一个线程程序是,就自动产生一个线程,主方法正式在这个线程上运行的。当不在启动其他线程时,该程序就是单线程程序。主方法线程启动有Java虚拟机负责,程序员负责自己的线程。在main方法中,使线程执行需要调用Thread类中的start()方法,start()方法调用被覆盖的run()方法,如果不调用start()方法,线程永远不会启动,在主方法中没有调用start()方法之前,Thread对象只是一个实例,而不是线程;start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。

二、实现Runnable接口

采用Runnable也是非常常见的一种,我们只需要重写run方法即可。

public class ThreadByRunnable {

	public static void main(String[] args) {
		Bank bank = new Bank();
		bank.setMoney(300);
		Thread threadOne = new Thread(bank);
		threadOne.setName("One");
		Thread threadTwo = new Thread(bank);
		threadTwo.setName("Two");
		threadOne.start();
		threadTwo.start();
	}
}

class Bank implements Runnable{
	private int number = 0;
	public void setMoney(int m) {
		number = m;
	}
	@Override
	public void run() {
		while(true) {
			String name = Thread.currentThread().getName();
			if(name.equals("One")) {
				if(number <= 160) {
					System.out.println(name+"进入死亡状态");
					return;	//threadOne的run方法结束
				}
				number = number+10;
				System.out.println("我是"+name+"现在number="+number);
			}
			if(Thread.currentThread().getName().equals("Two")) {
				if(number <= 0) {
					System.out.println(name+"进入死亡状态");
					return;
				}
				number = number-100;
				System.out.println("我是"+name+"现在number="+number);
			}
			try {
				Thread.sleep(800);
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
		
	}
	
}

使用Runnable接口启动线程的步骤如下:

(1)建立Runnable对象。

(2)使用参数为Runnable对象的构成方法创建Thread实例。

(3)调用start()方法启动线程

Bank类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的础。

三、继承Thread类和实现Runnable接口的比较

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享,适合多个相同的程序代码的线程去处理同一个资源,可以避免java中的单继承的限制,增加程序的健壮性,代码可以被多个线程共享,代码和数据独立;还有就是线程池只能放入实现Runable,不能直接放入继承Thread的类。

四、线程的生命周期

线程具有的生命周期,包含7种状态,分别为出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态。出生状态就是线程被创建时处于的状态,在用户使用线程实例调用start()方法之前线程都是处于出生状态;当用户调用start()方法后,线程处于就绪状态(可执行状态);当线程得到系统资源后就进入运行状态。一旦线程进入可执行状态,它就会在 就绪与运行状态下转换,同时也可能进入等待、休眠、阻塞或死亡状态。当处于运行状态下的线程调用Thread类中的wait()方法时,该线程便进入了等待状态,进入等待状态的线程必须调用Thread类中的notify()方法才能被唤醒;当线程调用Thread类中的sleep()方法时,则会进入休眠状态。如果一个线程在运行状态下发出输入/输出的请求,该线程将会进入阻塞状态,在其等待输入/输出结束时线程进入就绪状态,对于阻塞的线程;来说,即使系统资源空闲,线程依旧不能回到运行状态。当线程的run()方法执行完毕时,线程进入死亡状态。



虽然多线程看起来像同时执行,但事实上在同一时间上只有一个线程被执行,只是线程之间切换太快,所以才会使人产生线程时同时进行的假象。在Windows操作系统中,系统会个每个线程分配CPU时间片,一旦CPU时间片结束就会将当前线程换到下个线程,即使该线程没有结束。

可以总结出使线程处于就绪状态的几种方法:

1.调用sleep()方法;

2.调用wait()方法;

3等待输入/输出完成

当线程处于就绪状态后,可以用以下方法使线程再次进入运行状态:

1.调用notify()方法;

2.调用notifyall方法;

3.调用interrupt()方法;

4.休眠时间结束;

5.输入/输出结束;

五、操作线程的方法

(一)sheep()方法;

sheep()方法需要一个参数用于指定该线程的休眠时间,该时间以毫秒为单位。由于sleep()方法的执行有可能抛出InterruptedException异常,所以将sleep()方法的调用放在try_catch语句中,虽然使用了sleep()方法的线程在一段时间内会醒来,但并不保证他醒来后进入运行状态,只能保证他进入就绪状态。

try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

(二)join()方法;

如果当前程序为多线程程序,假如存在一个线程A,现在需要插入线程B,并要求B先执行完,然后继续执行A,此时可以使用Thread类中join()方法来完成。

以下一个实例为上面的进度条要等待下面的进度条完成才可以继续。

public class JoinTest extends JFrame {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Thread threadA; // 定义两个线程
	private Thread threadB;
	final JProgressBar progressBar = new JProgressBar(); // 定义两个进度条组件
	final JProgressBar progressBar2 = new JProgressBar();
	int count = 0;
	
	public static void main(String[] args) {
		init(new JoinTest(), 100, 100);
	}
	
	public JoinTest() {
		super();
		// 将进度条设置在窗体最北面
		getContentPane().add(progressBar, BorderLayout.NORTH);
		// 将进度条设置在窗体最南面
		getContentPane().add(progressBar2, BorderLayout.SOUTH);
		progressBar.setStringPainted(true); // 设置进度条显示数字字符
		progressBar2.setStringPainted(true);
		// 使用匿名内部类形式初始化Thread实例子
		threadA = new Thread(new Runnable() {
			int count = 0;
			
			public void run() { // 重写run()方法
				while (true) {
					progressBar.setValue(++count); // 设置进度条的当前值
					try {
						Thread.sleep(100); // 使线程A休眠100毫秒
						threadB.join(); // 使线程B调用join()方法
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		});
		threadA.start(); // 启动线程A
		threadB = new Thread(new Runnable() {
			int count = 0;
			
			public void run() {
				while (true) {
					progressBar2.setValue(++count); // 设置进度条的当前值
					try {
						Thread.sleep(100); // 使线程B休眠100毫秒
					} catch (Exception e) {
						e.printStackTrace();
					}
					if (count == 100) // 当count变量增长为100时
						break; // 跳出循环
				}
			}
		});
		threadB.start(); // 启动线程B
	}
	
	// 设置窗体各种属性方法
	public static void init(JFrame frame, int width, int height) {
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(width, height);
		frame.setVisible(true);
	}
}

(三)interrupt()方法;

不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!

public class Thread_interrupt {

	public static void main(String[] args) {
		ClassRoom room = new ClassRoom();
		room.student.start();
		room.teacher.start();

	}
}
class ClassRoom implements Runnable{
	Thread student,teacher;
	ClassRoom(){
		teacher = new Thread(this);
		student = new Thread(this);
		teacher.setName("雷老师");
		student.setName("张爱睡");
	}
	@Override
	public void run() {
		if(Thread.currentThread() == student) {
			try {
				System.out.println(student.getName()+"正在睡觉,不听课");
				Thread.sleep(1000*60*60);
			} catch (InterruptedException e) {
				System.out.println(student.getName()+"被老师叫醒了");
			}
			System.out.println(student.getName()+"开始听课");
		}
		else if(Thread.currentThread() == teacher) {
			for(int i = 0; i <= 3; i++ ) {
				System.out.println("上课");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			student.interrupt(); 
		}
	}
	
}

(四)wait()方法

Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

(五)yield()方法

Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知他可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。

六、线程优先级

调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。

Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
          线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
          线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
          分配给线程的默认优先级,取值为5。
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。


七、线程同步

在单线程程序中,每次只能做一件事,后面的事情需要等待前面 的事情完成后才可以进行,但是使用多线程时,就会发生两个线程抢占资源的问题,所以在多线程编程中需要防止资源访问的冲突。Java提供了线程同步的机制来防止资源访问的冲突。

下面来看一个火车售售票例子;

public class ThreadSafeTest implements Runnable {
	int num = 10; // 设置当前总票数
	
	public void run() {
		while (true) {
			if (num > 0) {
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("tickets" + num--);
			}
		}
	}
	
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest(); // 实例化类对象
		Thread tA = new Thread(t); // 以该类对象分别实例化4个线程
		Thread tB = new Thread(t);
		Thread tC = new Thread(t);
		Thread tD = new Thread(t);
		tA.start(); // 分别启动线程
		tB.start();
		tC.start();
		tD.start();
	}
}

运行出来最后 剩下的票为负值,这是因为同时创建4个线程,这4个线程执行run()方法,在num变量为1时,线程1、2、3、4都对num变量具有储存功能,当线程1执行run()方法时,还没来得及做递减操作,就指定它调用sleep()方法进入就绪状态,这时线程2、3、4都进入run()方法,发现num值依然大于0,但此时线程1休眠时间已到,将num变量值递减,同时线程2、3、4也都对nun进行递减操作,从而产生了负值。

如何解决资源共享问题呢?这时就需要给共享资源加上一把锁。在Java中提供了同步机制,可以有效防止资源冲突,同步机制使用关键字synchronized。

1.同步块

public class ThreadSafeTest implements Runnable {
	int num = 10; // 设置当前总票数
	
	public void run() {
               
		while (true) {
                     synchronized(""){
			if (num > 0) {
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("tickets" + num--);
			}
		}
	}
}	
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest(); // 实例化类对象
		Thread tA = new Thread(t); // 以该类对象分别实例化4个线程
		Thread tB = new Thread(t);
		Thread tC = new Thread(t);
		Thread tD = new Thread(t);
		tA.start(); // 分别启动线程
		tB.start();
		tC.start();
		tD.start();
	}
}

2.同步方法

语法:synchronized void f(){}

当某个对象调用了同步方法,该对象的其他方法必须等待该同步方法执行完毕后才执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生就业服务平台管理系统按照操作主体分为管理员和用户。管理员的功能包括学生档案管理、字典管理、试卷管理、试卷选题管理、试题表管理、考试记录表管理、答题详情表管理、错题表管理、法律法规管理、法律法规收藏管理、法律法规留言管理、就业分析管理、论坛管理、企业管理、简历管理、老师管理、简历投递管理、新闻资讯管理、新闻资讯收藏管理、新闻资讯留言管理、学生信息管理、宣传管理、学生管理、职位招聘管理、职位收藏管理、招聘咨询管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 大学生就业服务平台管理系统可以提高大学生就业服务平台信息管理问题的解决效率,优化大学生就业服务平台信息处理流程,保证大学生就业服务平台信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理新闻信息,管理大学生就业服务平台信息,包括考试管理,培训管理,投递管理,薪资管理等,可以管理新闻信息。 考试管理界面,管理员在考试管理界面可以对界面显示,可以对考试信息的考试状态进行查看,可以添加新的考试信息等。投递管理界面,管理员在投递管理界面查看投递种类信息,投递描述信息,新增投递信息等。新闻信息管理界面,管理员在新闻信息管理界面新增新闻信息,可以删除新闻信息。新闻信息类型管理界面,管理员在新闻信息类型管理界面查看新闻信息的工作状态,可以对新闻信息的数据进行导出,可以添加新新闻信息的信息,可以编辑新闻信息信息,删除新闻信息信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值