学习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之线程的主要内容,如果未能解决你的问题,请参考以下文章