例如人体同时进行呼吸、血液循环、思考问题等活动,用户既可以使用计算机听歌,也可以使用它打印文件,而这些活动完全可以同时进行,这种思想放在Java中被称为并发,而将并发完成的每一件事情称为线程。
Java语言提供并发机制,程序员可以在程序中执行多个线程,每一个线程完成一个功能,并与其他线程并发执行,这种机制被称为多线程。
1、继承Thread类
Thread类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立Thread实例。
(1)继承Thread类创建新线程:
public class ThreadTest extends Therad{
}
(2)使用run()方法编写线程功能
public void run(){
}
(3)strart()方法开始执行线程
public static void main(String[] args){
new ThreadTest().start();
}
例子:
package bao;
public class Demo1 extends Thread { // 创建文件
private int cou=10;
public void run() { //重写run()方法
while(true) {
System.out.print(cou+" "); //打印cou变量
if(--cou==0) { //使cou变量自减,当cou等于0时退出循环
return;
}
}
}
public static void main(String[] args) {
new Demo1().start(); //开始执行线程
}
}
/*
* 输出结果:
10 9 8 7 6 5 4 3 2 1
* */
2、实现Runnable接口
到目前为止,线程都是通过扩展Thread类来创建的,如果程序员需要继承其他类(非Thread类)并使该程序可以使用线程,就需要使用Runnable接口。
实现Runnable接口的语法如下:
public class Thread extends Object implements Runnable
Runnable接口启动线程的步骤如下:
(1)建立Runnable对象。
(2)使用参数为Runnable对象的构造方法创建Thread实例。
(3)调用statrt()方法启动线程。
例子:
package bao;
import java.awt.Container;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
public class Demo1 extends JFrame { // 创建文件
private JLabel ji=new JLabel(); //声明JLabel对象
private static Thread t; //声明线程对象
private int cou=0; //声明计算变量
private Container container=getContentPane(); //声明容器
public Demo1() {
setBounds(300,200,290,100); //绝对定位窗体大小与位置
container.setLayout(null); //不使用布局管理
URL url=Demo1.class.getResource("3.gif"); //获取图片的URL
Icon icon=new ImageIcon(url); //实例化Icon
ji.setIcon(icon); //将图标放置在标签的最左方
ji.setHorizontalAlignment(SwingConstants.LEFT); //设置图片在标签最左方
ji.setBounds(10, 10, 200, 50); //设置标签位置与大小
ji.setOpaque(true);
t=new Thread(new Runnable() { //定义匿名内部类,该类实现Runnable接口
public void run() { //run()方法
while(cou<=200) { //设置循环条件
ji.setBounds(cou, 10, 200, 50); //将标签的坐标用变量表示
try {
Thread.sleep(100); //使用线程的休眠 1000毫秒
}catch(Exception e){
e.printStackTrace();
}
cou +=4; //每一次使横坐标增加4
if(cou==200) {
cou=10;
}
}
}
});
t.start(); //启动线程
container.add(ji); //将标签添加到容器中
setVisible(true); //使窗体可见
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); //设置窗体的关闭方式
}
public static void main(String[] args) {
new Demo1(); //实例化一个Demo1对象
}
}
结果:
图标向右边移动
线程具有生命周期,其中包含7种状态,分别为出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态。
1、线程的休眠 ( sleep() )
一种能控制线程行为的方法是调用sleep()方法,sleep()方法需要一个参数用于指定该线程休眠的时间,该时间使用毫秒为单位。
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
例子:
package bao;
public class Demo1 extends Thread { // 创建文件
private int cou=10;
public void run() { //重写run()方法
while(true) {
try {
Thread.sleep(1000); //线程休眠1000毫秒
}catch(Exception e) {
e.printStackTrace();
}
System.out.print(cou+" "); //打印cou变量
if(--cou==0) { //使cou变量自减,当cou等于0时退出循环
return;
}
}
}
public static void main(String[] args) {
new Demo1().start(); //开始执行线程
}
}
/*输出结果:
10 9 8 7 6 5 4 3 2 1
ps:由于进行了休眠,所以每1000毫秒输出一个数。
*/
2、线程的加入 ( join() )
如果当前某程序为多线程程序,假如存在一个线程A,现在需要插入线程B,并要求线程B先执行完毕,然后再继续执行线程A,此时可以使用Thread类中的join()方法来完成。
package bao;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
public class Demo1 extends JFrame { // 创建文件
private Thread threadA; //定义A,B两个线程
private Thread threadB;
final JProgressBar progressBar=new JProgressBar(); //定义两个进度条组
final JProgressBar progressBar2=new JProgressBar();
int cou=0;
public static void main(String[]args) {
init(new Demo1(),100,100);
}
public Demo1() {
super();
getContentPane().add(progressBar,BorderLayout.NORTH); // 将进度条设置在窗体最北面
getContentPane().add(progressBar2,BorderLayout.SOUTH); // 将进度条设置在窗体最南面
progressBar.setStringPainted(true); // 设置进度条显示数字字符
progressBar2.setStringPainted(true);
// 使用匿名内部类形式初始化Thread实例子
threadA=new Thread(new Runnable() {
int cou=0;
public void run() { // 重写run()方法
while(true) {
progressBar.setValue(++cou);
try {
Thread.sleep(100); // 使线程A休眠100毫秒
threadB.join(); // 使线程B调用join()方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
threadA.start(); //启动线程A
threadB=new Thread(new Runnable() {
int cou=0;
public void run() { // 重写run()方法
while(true) {
progressBar2.setValue(++cou); // 设置进度条的当前值
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if(cou==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);
}
}
结果:
等线程B的值到100时启动线程A
3、线程的中断
在以往时候会使用stop()方法停止线程,但当前版本的JDK早已废除了stop()方法,同时也不建议使用stop()方法来停止一个线程的运行。现在提倡在run()方法中使用无线循环的形式,运用布尔类型来控制循环停止。 线程对象:interrupt();
package bao;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
public class Demo1 extends JFrame { // 创建文件
private static final long serialVersionUID = 1L;
Thread thread;
public static void main(String[] args) {
init(new Demo1(), 100, 100);
}
public Demo1() {
final JProgressBar progressBar = new JProgressBar(); // 创建进度条
// 将进度条放置在窗体合适位置
getContentPane().add(progressBar, BorderLayout.NORTH);
progressBar.setStringPainted(true); // 设置进度条上显示数字
thread = new Thread(new Runnable() {
int count = 0;
public void run() {
while (true) {
progressBar.setValue(++count); // 设置进度条的当前值
try {
Thread.sleep(1000); // 使线程休眠1000豪秒
// 捕捉InterruptedException异常
} catch (InterruptedException e) {
System.out.println("当前线程序被中断");
break;
}
}
}
});
thread.start(); // 启动线程
thread.interrupt(); // 中断线程
}
public static void init(JFrame frame, int width, int height) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(width, height);
frame.setVisible(true);
}
}
/*输出结果:
当前线程序被中断
*/
一直都是进度,因为被禁止了。
4、线程的礼让 ( yield() )
Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态下的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
五、线程的优先级
每个线程都具有各自的优先级,线程的优先级可以在程序中表明该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。
Thread类中包含的成员变量代表了线程的某些优先级,比如
Thread.MIN_PRIORITY(常数1)
Thread.MAX_PRIORITY(常数2)
Thread.NORM_PRIORITY(常数5)。其中每个线程的优先级都在Thread.MIN_PRIORITY~Thread.MAX_PRIORITY之间,在默认情况下其优先级都是Thread.NORM_ PRIORITY。每个新产生的线程都继承了父线程的优先级。
package bao;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
public class Demo1 extends JFrame { // 创建文件
private static final long serialVersionUID = 1L;
private Thread threadA;
private Thread threadB;
private Thread threadC;
private Thread threadD;
public Demo1() {
getContentPane().setLayout(new GridLayout(4, 1));
// 分别实例化4个线程
final JProgressBar progressBar = new JProgressBar();
final JProgressBar progressBar2 = new JProgressBar();
final JProgressBar progressBar3 = new JProgressBar();
final JProgressBar progressBar4 = new JProgressBar();
getContentPane().add(progressBar);
getContentPane().add(progressBar2);
getContentPane().add(progressBar3);
getContentPane().add(progressBar4);
progressBar.setStringPainted(true);
progressBar2.setStringPainted(true);
progressBar3.setStringPainted(true);
progressBar4.setStringPainted(true);
threadA = new Thread(new MyThread(progressBar));
threadB = new Thread(new MyThread(progressBar2));
threadC = new Thread(new MyThread(progressBar3));
threadD = new Thread(new MyThread(progressBar4));
setPriority("threadA", 5, threadA);
setPriority("threadB", 5, threadB);
setPriority("threadC", 4, threadC);
setPriority("threadD", 3, threadD);
}
// 定义设置线程的名称、优先级的方法
public static void setPriority(String threadName, int priority,Thread t) {
t.setPriority(priority); // 设置线程的优先级
t.setName(threadName); // 设置线程的名称
t.start(); // 启动线程
}
public static void main(String[] args) {
init(new Demo1(), 100, 100);
}
public static void init(JFrame frame, int width, int height) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(width, height);
frame.setVisible(true);
}
private final class MyThread implements Runnable { // 定义一个实现Runnable接口的类
private final JProgressBar bar;
int count = 0;
private MyThread(JProgressBar bar) {
this.bar = bar;
}
public void run() { // 重写run()方法
while (true) {
bar.setValue(count += 10); // 设置滚动条的值每次自增10
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("当前线程序被中断");
}
}
}
}
}
看着好像同步进行,其实threadA和threadB等级比较高,所以优先进行。其次是threadC,threadD进行。
1、线程安全
线程安全问题:实质上线程安全问题来源于两个线程同时存取单一对象的数据。例如火车票只剩下一张,但有两个人同时进行购买火车票,那么线程只能分配到其中一个人手中,并且要让火车票不能出现小于0的状况。
package bao;
public class Demo1 implements Runnable {
int num=10;
public void run() {
while(true) {
if(num>0) {
try {
Thread.sleep(1000);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("线程:"+num--);
}
}
}
public static void main(String[]args){
Demo1 d=new Demo1();
Thread dA=new Thread(d); //分别实例化4个线程
Thread dB=new Thread(d);
Thread dC=new Thread(d);
Thread dD=new Thread(d); //分别启动线程
dA.start();
dB.start();
dC.start();
dD.start();
}
}
/*输出结果:
线程:10
线程:9
线程:8
线程:7
线程:6
线程:5
线程:4
线程:3
线程:2
线程:1
线程:0
线程:-1
线程:-2
* */
2、线程同步机制(synchronized)
基本上所有解决多线程资源冲突问题都会采用给定时间只允许一个线程访问共享资源,这时就需要给共享资源上一道锁。例如:例如吃个人吃饭,要这个人吃完这口饭才能继续进行下一口饭。
同步机制使用:关键字synchronized
package bao;
public class Demo1 implements Runnable {
int num=10;
public void run() {
while(true) {
if(num>0) {
synchronized ("") { //同步机制使用synchronized
try {
Thread.sleep(1000);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("线程:"+num--);
}
}
}
}
public static void main(String[]args){
Demo1 d=new Demo1();
Thread dA=new Thread(d); //分别实例化4个线程
Thread dB=new Thread(d);
Thread dC=new Thread(d);
Thread dD=new Thread(d); //分别启动线程
dA.start();
dB.start();
dC.start();
dD.start();
}
}
/*输出结果:
线程:10
线程:9
线程:8
线程:7
线程:6
线程:5
线程:4
线程:3
线程:2
线程:1
线程:0
线程:-1
线程:-2
* */