学习Java之线程

Posted So istes immer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习Java之线程相关的知识,希望对你有一定的参考价值。

1.基本概念

程序”代表一个静态的对象,是内含指令和数据的文件,存储在磁盘或其他存储设备中
进程”代表一个动态的对象,是程序的一个执行过程,存在于系统的内存中。一个进程对应于一个程序
线程”是运行于某个进程中,用于完成某个具体任务的顺序控制流程,有时被称为轻型进程

进程和线程的区别
①我们把操作系统的多个任务称为进程,而程序中的多任务则称为线程
② 线程同进程一样,是一个动态的概念和一个动态的执行过程,但是线程比进程的内涵要小一个等级,一般一个进程(应用程序)包含一个或多个线程
最基本的区别:每个进程都拥有一组完整的属于自己的变量,而线程则共享这些数据
④每个进程都有自己的内存空间,线程需要在进程的内存地址空间中运行。
⑤进程可独立运行,线程不拥有系统资源,所以不能独立运行。
⑥进程是计算机多任务操作系统为任务分配资源最小单位,而线程是多任务操作系统用于分配计算机CPU时间片最小单位
⑦线程的优势在于创建和注销线程的开销比运行新的进程少得多
⑧进程之间是互相独立的,一个进程一般不允许访问其他进程的内存空间,因此,进程间通信非常困难。与此相比,线程间的通信要快得多,也方便得多

线程有两个缺陷:死锁饥饿
死锁:一个或者多个线程,在一个给定的任务中,协同作用,互相干涉,从而导致一个或者更多线程永远等待下去
饥饿:一个线程永久性地占有资源,使得其他线程得不到该资源

每个线程都有其自己的堆栈程序计数器(PC)
堆栈:用于跟踪线程的上下文(上下文是当线程执行到某处时,当前的局部变量的值)
PC:用于跟踪线程正在执行的指令

一个线程或执行上下文由三个主要部分组成:
① 一个虚拟处理机
② CPU执行的代码
③ 代码操作的数据

2.创建线程

通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法
①继承Thread类并重载run方法
具体步骤
a、生成Thread类的子类
class MyThread extends Thread
b、在子类中覆盖run()方法
public void run()
c、生成子类的对象,并且调用start()方法启动新线程。
MyThread thread = new MyThread();
thread.start();
start()方法将调用run()方法执行线程

class FirstThread extends Thread {
  public void run() {...}
}
class SecondThread extends Thread {
 public void run() {...}
}
public class ThreadTest1 {
  public ThreadTest1() {
	  FirstThread first = new FirstThread();
	  SecondThread second = new SecondThread();
	  first.start();second.start();
	}
	public static void main(String[] args) {
	  new ThreadTest1();
	}
}

②通过定义实现Runnable接口的类,进而实现run方法
具体步骤
a、定义一个类实现Runnable接口
class FirstThread implements Runnable
b、并且在该类中实现run()方法
public void run()
c、生成这个类的对象
FirstThread first = new FirstThread();
d、用Thread(Runnable target)构造函数生成Thread对象,然后调用start()方法启动线程。
Thread thread1 = new Thread(first);
thread1.start();

线程开始执行时,它在public void run()方法中执行。
该方法是定义的线程执行起点,像应用程序从main()开始
一样

public class SimpleRunnable implements Runable{ 
	private String message;
	public static void main(String args[]){ 
	  SimpleRunnable r1=new SimpleRunnable("Hello"); 
	  Thread t1=new Thread(r1); 
	  t1.start(); 
	} 
	public SimpleRunnable(String message){ 
	  this.message = message; 
	} 
	public void run(){ 
	  System.out.println(message); 
	} 
}

3. java.lang.Thread 类中的五个方法

● run():该方法用于线程的执行。你需要重载该方法,以便让线程做特定的工作
● start():该方法使得线程启动run()方法
● stop():该方法同start()方法的作用相反,用于停止线程的运行
● suspend():该方法同stop()方法不同的是,它并不终止未完成的线程,而只是挂起线程,以后还可恢复
● resume():该方法重新启动已经挂起的线程

4.方法的选择

由于Java技术只允许单一继承,所以如果你已经继承了Thread类,你就不能再继承其他任何类,例如Applet。在某些情况下,这会使你只能采用实现Runnable的方法

5.线程的状态

new(初始态):一个线程在调用new()方法之后,调用start()方法之前所处的状态。在初始态中,可以调用start()和stop()方法
rRunnable(可运行状态):一旦线程调用了start()方法,线程就转到Runnable()状态。注意,如果线程处于Runnable状态,它也有可能不在运行,这是因为还存在优先级和调度问题
blocked(阻塞/挂起状态):线程处于阻塞状态。这是由两种可能性造成的:①因挂起而暂停②由于某些原因而阻塞,例如等待IO请求的完成等
dead(终止状态):线程转到退出状态。这有两种可能性:run()方法执行结束;调用了stop()方法

6.管理线程

在Java中,线程是抢占式的,但并不一定是分时的。
抢占式调度模型是指可能有多个线程是可运行的,但只有一个线程在实际运行。 这个线程会一直运行,直至它不再是可运行的,或者另一个具有更高优先级的线程成为可运行的

线程类提供了很多对线程进行操作的重要方法:sleep()、join()、wait()和notify()
sleep()方法是使线程停止一段时间的方法
join()方法使得一个线程等待另外一个线程结束后再执行
线程API isAlive()同join()相关联时,是很有用的。
一个线程在start之后,在stop之前的某时刻处于isAlive状态
yield()方法只会给相同优先级线程一个执行的机会
setPriority(int)方法来设置线程的优先级大小

Thread threadone=new Thread();
Thread threadtwo=new Thread();
threadone.setPriority(6); // 设置threadone的优先级为6
threadtwo.setPriority(3); // 设置threadtwo的优先级为3
threadone.start(); threadtwo.start();

7.线程组

线程组就是一种可以管理一组线程的类
构造
①使用构造方法ThreadGroup()
ThreadGroup g = new ThreadGroup(groupName);
②也可以用Thread类的构造方法往一个指定的线程组里添加新的线程:
Thread t = new Thread(g, threadName);

activeCount()方法用于检测某个指定线程组是否有线程处于活动状态:
if (g.activeCount() = = 0){// 线程g的所有线程都已停止}
● 要中断一个线程组中的所有线程,可以调用 ThreadGroup类的方法interrupt()
g.interrupt();

8.锁

在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。
解决方法:在线程使用一个资源时为其加锁即可。
访问资源的第一个线程为其加上锁以后,其他线程便不能再使用那个资源,除非被解锁

9.练习

1.模拟龟兔赛跑

/**
 * 
 * 修改成乌龟和兔子赛跑
 * 
 */
import java.awt.Color;

import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.EmptyBorder;

public class JProcessBarDemo extends JFrame{

	private static final long serialVersionUID = 1L;
	private JProgressBar processBar,processBar2;
	private JButton button;

	public JProcessBarDemo(){
		setTitle("The Progress Bar Test");		//设置窗体标题
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(250, 150);
		setLocationRelativeTo(null);	  		//将窗口置于屏幕中央
		JPanel contentPanel = new JPanel();		//新建一个面板容器
		button = new JButton("running start");
		contentPanel.setBorder(new EmptyBorder(5,5,5,5));//设置内容面板边框
		setContentPane(contentPanel);			//应用(使用)内容面板
		contentPanel.setLayout(new FlowLayout(FlowLayout.CENTER,5,5));//设置为流式布局
		Label rab = new Label("乌龟:");	    	//设置兔子的标签
		Label tor = new Label("兔子:");			//设置乌龟的标签
		processBar = new JProgressBar();		//创建兔子的进度条
		processBar.setStringPainted(true);		//设置进度条上的字符串显示,false则不能显示
		processBar.setBackground(Color.GREEN);
		processBar2 = new JProgressBar();		//创建乌龟的进度条
		processBar2.setStringPainted(true);		
		processBar2.setBackground(Color.GREEN);
		button.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				new Thread(){
			public void run(){
				for (int i = 0; i < 101; i++) {
					try{
						Thread.sleep(100);		//让当前线程休眠0.1ms
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					processBar.setValue(i);
				}
			}
		}.start();
		        new Thread(){
		    public void run(){
				for (int i = 0; i < 101; i+=2) {//兔子的速度设为乌龟的两倍
					try{
						Thread.sleep(200);		//让当前线程休眠0.2ms
					}catch(InterruptedException e){
						e.printStackTrace();
					}
					processBar2.setValue(i);
				}
			}
		}.start();
			}
		});
		//创建线程显示进度
		
		contentPanel.add(tor);
		contentPanel.add(processBar);
		contentPanel.add(rab);
		contentPanel.add(processBar2);
		contentPanel.add(button);
	}
	public static void main(String[] args){
		JProcessBarDemo J = new JProcessBarDemo();
		J.setVisible(true);
	}
}

2.六个框,同时随机显示数字,按停止键,显示当前的六个数字

package thread_test;

import java.awt. *;
import java.awt.event. *;

class countbutton extends Button implements Runnable {
	public boolean exit = false;
	
	public countbutton(String s) {
		super(s);
	}
	
	public void run() {
		while(!exit) {
			try {
				this.setLabel("" + (int)(Math.random()*10));
				Thread.sleep((int)(1000 * Math.random()));
			}catch(Exception e) {
				
			}
		}
	}	
}

public class CountFrame extends Frame implements ActionListener{
	countbutton b1,b2,b3,b4,b5,b6;
	
	public CountFrame() {
		setLayout(null);			 	//不使用布局管理
		b1 = new countbutton("first");
		b1.setBounds(30, 50, 50, 80);add(b1);
		b2 = new countbutton("second");
		b2.setBounds(100, 50, 50, 80);add(b2);
		b3 = new countbutton("third");
		b3.setBounds(170, 50, 50, 80);add(b3);
		b4 = new countbutton("fourth");
		b4.setBounds(240, 50, 50, 80);add(b4);
		b5 = new countbutton("fifth");
		b5.setBounds(310, 50, 50, 80);add(b5);
		b6 = new countbutton("sixth");
		b6.setBounds(380, 50, 50, 80);add(b6);
		(new Thread(b1)).start();
		(new Thread(b2)).start();
		(new Thread(b3)).start();
		(new Thread(b4)).start();
		(new Thread(b5)).start();
		(new Thread(b6)).start();
		Button btnStop = new Button("停止");
		add(btnStop);
		btnStop.setBounds(350, 150, 80, 40);
		btnStop.addActionListener(this);
	}
	
	//终止六个线程
	public void actionPerformed(ActionEvent e) {
		b1.exit=b2.exit=b3.exit=b4.exit=b5.exit=b6.exit=true; //通过设置exit变量来结束run函数里面的while循环
	}
	
	public static void main(String args[]) {
		Frame f = new CountFrame();
		f.setBounds(350, 200, 450, 250);
		f.setVisible(true);
		f.addWindowListener(new WindowAdapter() {		//添加窗口事件监听
			public void windowClosing(WindowEvent e) {	
				System.exit(0);							//添加点击窗口的关闭按钮时发生的事件-退出
			}
		});
	}
}

测试结果
在这里插入图片描述

以上是关于学习Java之线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

Java多线程之JUC包:Semaphore源码学习笔记

Java线程池详解

java SpringRetry学习的代码片段

java学习笔记之TCP实现的简单聊天

Java源码之 java.util.concurrent 学习笔记01