线程简介
//程序可以执行多个线程,,每个线程完成一个功能,并与其它线程并发执行,称为多线程。
//以多线程在Windows操作系统为例,它是多任务操作系统,它以进程为单位,一个进程是包含有自身地址的程序。每个独立执行的程序都被称为进程。Java语言提供多线程机制,它可以同时完成多段代码的操作。
实现线程的俩种方式
//Java中主要提供了俩种实现线程的方式,分别继承java.lang.Thread和java.lang.Runnable
继承Thread类
//Thead是一个类,从这个类中实例化的对象代表线程。程序员启动一个新的线程需要建立一个Thread实例,俩种构造方法如下:
public Thread(); //创建一个新的线程对象
public Thread(String threadName); //创建一个名称为threadName的线程对象
package 多线程;
public class ThreadTest extends Thread {
private int count = 10;
//重写run()方法
public void run() {
while (true) {
System.out.println(count + " ");
if (--count == 0) {
return;
}
}
}
public static void main(String args[]) {
new ThreadTest().start();
}
}
//完成线程真正功能代码实在run()中,当一个类继承了Thread类后,就可以在该类中覆盖run()方法。即要将实现该线程功能得代码写入run()方法中,同时调用Thread类中的start()方法执行线程。run()方法语法必须如下:
//public void run() {
}
//当执行一个线程程序时,就会自动产生一个线程。主方法正是在这个线程上运行的。
//主方法线程启动有Java虚拟机负责,程序员负责启动自己的线程。
//start()方法调用被覆盖的run()方法,如果不调用start(0方法,线程将永远不会被启动。
//在主方法没有调用start()之前。Thread对象只是一个实例,并不是一个真正的线程。
实现Runnable接口
//到目前为止,程序都是拓展Thread类创建的,如果程序员需要继承其他类,而且还要使其当前类实现多线程,可以通过Runnable接口来实现。
//Java语言不支持多继承。Runnable接口语法如下:
public class Thread extends Object implements Runnable
//实现Runnable接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联。Thread类中俩中构造方法如下:
public Thread(Runnable target);
public Thread(Runnable target,String name);
//来个构造方法的参数都与Runnable实例相关联。
//使用Runnable接口启动新的线程步骤如下:
//①建立Runnable对象
//②使用参数为Runnable的对象构造方法创建Thread实例
//③调用start()方法启动线程
package 多线程;
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class SwingAndThread extends JFrame {
//声明JLabel对象
private JLabel jl = new JLabel();
//声明线程对象
private static Thread t;
//声明计数变量
private int count = 0;
//声明容器
private Container c = getContentPane();
public SwingAndThread() {
//绝对定位窗体大小与位置
setBounds(300,200,250,100);
//使此窗体不用任何布局管理器
c.setLayout(null);
//获取图片URL
URL url = SwingAndThread.class.getResource("./主图5扣橘子.jpg");
//实例化一个ICON
Icon icon = new ImageIcon(url);
//将图片放置在标签中
jl.setIcon(icon);
//设置图片在标签的最左方
jl.setHorizontalAlignment(SwingConstants.LEFT);
jl.setBounds(10,10,200,5);
jl.setOpaque(true);
//定义匿名内部类,该类实现Runnable接口
t = new Thread(new Runnable() {
@Override
public void run() {
//设置循环条件
while (count <= 200) {
//将标签的横坐标用变量表示
jl.setBounds(count, 10, 200, 50);
try {
//使线程休眠1000毫秒
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//使横坐标每次加4
count += 4;
if (count == 200) {
//当图标达到标签的最右面,使其返回到标签的最左面
count = 0;
}
}
}
});
//启动线程
t.start();
//将标签添加到容器中
c.add(jl);
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
public static void main(String args[]) {
//实例化一个SwingAndThread对象
new SwingAndThread();
}
}
//为了使图标具有滚动功能,需要在类的构造方法中创建Thread实例,在创建该类的同时需要Runnable对象作为Thread类构造方法的参数。然后使用内部类形式实现run()方法。
//启动一个新的线程,不是直接调用Thread子类对象的run()方法,而是调用Thread子类的start()方法,从而产生一个新线程。
线程的生命周期
//线程具有生病周期,具有7种状态:出生状态、就绪状态(可执行状态)、运行状态、等待状态、休眠状态、阻塞状态、死亡状态。
//出生状态就是程序被创建时的状态、用户实例化调用start()方法之前都处于出生状态,当用户调用start()方法之后,程序处于就绪状态。当线程得到系统资源后处于运行状态。
//当线程进入可执行状态,它就会在就绪状态和运行状态之间转换,同时也可能进入其他状态。当处于运行状态下的线程调用Thread类中的wait()方法时,线程便进入等待状态。进入等待状态中的线程必须调用notify()才能被唤醒。notifyAll()方法是将所有处于等待状态下的线程唤醒。调用sleep()方法时,程序处于休眠状态。当一个线程在运行状态下发出输入/输出请求时,线程将进入阻塞状态。在其等待输出输入结束是进入就绪状态。处于阻塞状态下,即使系统资源空闲,也不会进入运行状态。当线程run()方法执行完毕之后会进入死亡状态。
//虽然多线程看起来是同时进行,但是事实上在同一时间点只有一个线程被执行,只是线程之间的转换较快,当每个cpu时间片执行完毕之后,即使该线程没有结束也会转换到下一个线程。
操作线程的方法
线程的休眠
//sleep()方法的调用放在try-catch块中,否则可能会抛出Interrupte异常。此方法以毫秒为单位。
package 多线程;
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class SleepMethodTest extends JFrame {
private Thread t;
//定义颜色数组
private static Color[] colors = {Color.BLACK,Color.BLUE,Color.CYAN,
Color.GREEN,Color.ORANGE,Color.YELLOW,Color.RED,Color.PINK,Color.LIGHT_GRAY};
//创建随机对象
private static final Random random = new Random();
//获取随机颜色值得方法
private static Color getC() {
return colors[random.nextInt(colors.length)];
}
public SleepMethodTest() {
//创建匿名类线程对象
t = new Thread(new Runnable() {
//定义初始坐标
int x = 30;
int y = 50;
@Override
//覆盖线程接口方法
public void run() {
while (true) {
try {
//使线程休眠1秒
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//获取组件绘图上下文的对象
Graphics graphics = getGraphics();
//设置绘图yanse
graphics.setColor(getC());
//绘制直线并递增垂直坐标
graphics.drawLine(x, y, 100, y++);
if (y >= 80) {
y = 50;
}
}
}
});
}
public static void Init(JFrame jFrame, int width, int high) {
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setSize(width, high);
jFrame.setVisible(true);
}
public static void main(String args[]) {
Init(new SleepMethodTest(),100,100);
}
}
线程的加入
//加入当前线程为多线程程序,加入存在一个线程A,现在需要加入一个线程B,并要求B线程先执行完毕,然后在执行线程A。可以使用Thread类中的join()方法。
//当使用join()方法时加入到另一个线程时,另一个线程会等该线程执行完毕,在继续执行。
package 多线程;
import javax.swing.*;
import java.awt.*;
public class JoinTest extends JFrame {
//定义俩个线程
private Thread threadA;
private Thread threadB;
//定义俩个进度条
final JProgressBar jProgressBar = new JProgressBar();
final JProgressBar jProgressBar2 = new JProgressBar();
int count = 0;
public JoinTest() {
super();
//将进度条分别设置在窗体的最上面和最下面
Container c = getContentPane();
c.add(jProgressBar,BorderLayout.NORTH);
c.add(jProgressBar2,BorderLayout.SOUTH);
//设置进度条显示数字字符
jProgressBar.setStringPainted(true);
jProgressBar2.setStringPainted(true);
//使用匿名内部类定义形式初始化Thread实例
threadA = new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while (true) {
//设置进度条当前值
jProgressBar.setValue(++count);
try {
//使线程A休眠100毫秒
Thread.sleep(100);
//调用join()方法使线程B加入
threadB.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
//启动线程A
threadA.start();
threadB = new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while (true) {
//设置当前进度条的值
jProgressBar2.setValue(++count);
try {
//使线程B休眠100毫秒
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if (count == 100) {
break;
}
}
}
});
threadB.start();
}
public static void init(JFrame jFrame, int width, int high) {
jFrame.setSize(width, high);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
public static void main(String args[]) {
init(new JoinTest(), 100, 100);
}
}
线程的中断
//以往使用stop()方法作为线程的中断,但是JDK早已废除了这种方法。现在提倡在run()方法中使用无限循环形式,然后使用一个布尔类型来控制循环的停止。
package 多线程;
public class InterruptedTest implements Runnable{
private boolean isContinue = false;
public void run() {
//...
//当isContinue变成true是停止该线程
if (isContinue) {
break;
}
}
public static void main(String args[]) {
this.isContinue = true;
}
}
//注意:如果线程是因为使用了sleep()方法或是wait()方法进入了就绪状态,可以使用Thread类中的interrupt()方法使线程离开run(),同时结束进程,但是程序会抛出InterruptedException异常。用户可以在处理异常时完成线程的中断业务处理。
package 多线程;
import javax.swing.*;
import java.awt.*;
public class InterruputedSwing extends JFrame {
//定义一个进程
Thread thread;
public InterruputedSwing() {
super();
//定义一个进度条
final JProgressBar jProgressBar = new JProgressBar();
//定义一个容器
Container c = getContentPane();
//设置进度条上的数字
jProgressBar.setStringPainted(true);
//将进度条放置在容器的适当位置
c.add(jProgressBar,BorderLayout.NORTH);
thread = new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while (true) {
//设置当前进度条的值
jProgressBar.setValue(++count);
try {
thread.sleep(1000);
//捕捉InterruptedException异常
} catch (InterruptedException e) {
System.out.println("当前程序被中断");
break;
}
}
}
});
//启动线程
thread.start();
//中断线程
thread.interrupt();
}
//初始化窗体
public static void init(JFrame jFrame, int width, int high) {
jFrame.setSize(width, high);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
init(new InterruputedSwing(),100,100);
}
}
//由于调用了interrupted()方法所以抛出了InterruptedException异常
线程的礼让
//Thread类中提供了一种礼让机制,yeild()方法,它只是给当前正在运行程序一个提醒,告知它可以将资源礼让给其它线程。但这仅仅使一种暗示,没有任何一种机制会保证线程会进行资源礼让。
线程的优先级
//每个线程都有各自的优先级,多个线程处于就绪状态,系统会根据优先级来决定优先运行哪个线程。
package 多线程;
import javax.swing.*;
import java.awt.*;
public class PriorityTest extends JFrame {
//定义四个线程
private Thread threadA;
private Thread threadB;
private Thread threadC;
private Thread threadD;
//定义四个进度条
final JProgressBar jProgressBarA = new JProgressBar();
final JProgressBar jProgressBarB = new JProgressBar();
final JProgressBar jProgressBarC = new JProgressBar();
final JProgressBar jProgressBarD = new JProgressBar();
int count = 0;
public PriorityTest() {
super();
//定义一个容器
Container c = getContentPane();
//设置进度条可用数字字符
jProgressBarA.setStringPainted(true);
jProgressBarB.setStringPainted(true);
jProgressBarC.setStringPainted(true);
jProgressBarD.setStringPainted(true);
//设置进度条在窗体中的位置
c.add(jProgressBarA,BorderLayout.NORTH);
c.add(jProgressBarB,BorderLayout.EAST);
c.add(jProgressBarC,BorderLayout.WEST);
c.add(jProgressBarD,BorderLayout.SOUTH);
//分别实例化四个线程
threadA = new Thread(new MyThread(jProgressBarA));
threadB = new Thread(new MyThread(jProgressBarB));
threadC = new Thread(new MyThread(jProgressBarC));
threadD = new Thread(new MyThread(jProgressBarD));
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.setName(threadName);
t.setPriority(priority);
t.start();
}
//定义一个实现Runnable接口的类
private final class MyThread implements Runnable{
private final JProgressBar bar;
int count = 0;
private MyThread(JProgressBar bar) {
this.bar = bar;
}
//重写run()方法
public void run() {
while (true) {
//设置滚动条每次递增10
bar.setValue(count +=10);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("当前线程被中断");
}
}
}
}
public static void init(JFrame jFrame, int width, int high) {
jFrame.setSize(width, high);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
init(new PriorityTest(),100,10);
}
}
线程同步
//当俩个以上的线程抢占资源时,访问资源发生冲突。
线程安全
//线程安全问题来源于俩个线程同时存取单一对象的数据。
package 多线程;
import java.time.chrono.ThaiBuddhistEra;
import java.util.SplittableRandom;
public class ThreadSafeTest implements Runnable {
//设置当前总票数
int count = 10;
public void run() {
while (true) {
if (count > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("tickets" + count--);
}
}
}
public static void main(String args[]) {
//实例化该类对象
ThreadSafeTest t = new ThreadSafeTest();
//以该类对象分别实例化四个线程
Thread threadA = new Thread(t);
Thread threadB = new Thread(t);
Thread threadC = new Thread(t);
Thread threadD = new Thread(t);
//分别启动四个线程
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}
//四个线程都对于count变量具有存储功能,当count = 1时,当线程1执行run()方法,还没来几做递减操作,就调用sleep()方法就如就绪状态。这时线程2、3、4都进入了run()方法,发现count仍然大于0,但此时线程1的休眠时间到,将count做递减操作,2,3,4也做递减操作,从而产生了负值
线程同步机制
同步块
//在一定时间内只允许一个线程访问共享资源
//将资源放置在同步块中,这个同步块又称为临界区,使用synchronized关键字建立
synchronized(Object){
}
//案例:
package 多线程;
import java.time.chrono.ThaiBuddhistEra;
import java.util.SplittableRandom;
public class ThreadSafeTest implements Runnable {
//设置当前总票数
int count = 10;
public void run() {
while (true) {
//设置同步块
synchronized ("") {
if (count > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("tickets" + count--);
}
}
}
}
public static void main(String args[]) {
//实例化该类对象
ThreadSafeTest t = new ThreadSafeTest();
//以该类对象分别实例化四个线程
Thread threadA = new Thread(t);
Thread threadB = new Thread(t);
Thread threadC = new Thread(t);
Thread threadD = new Thread(t);
//分别启动四个线程
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}
//当将共享资源放置在同步块区域中,其它线程访问时,必须等到被释放才能进入。Object为任意一个对象,每个对象都存在一个标识位,并具有俩个值0和1,一个线程运行到同步块中首先检查该对象的标志位,若为0则表明同步块中其它线程在运行,处于就绪状态。知到同步块中的线程执行完代码之后,标志位为1该线程进入。
同步方法
//语法如下:
synchronized void f(){
}
//案例:
package 多线程;
public class ThreadSafeTest_02 implements Runnable{
int count = 10;
public synchronized void doit() {
if (count > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("tickets" + --count);
}
}
public void run() {
while (true) {
doit();
}
}
public static void main(String args[]) {
//实例化该类对象
ThreadSafeTest_02 t = new ThreadSafeTest_02();
//将该对象实例化四个线程
Thread threadA = new Thread(t);
Thread threadB = new Thread(t);
Thread threadC = new Thread(t);
Thread threadD = new Thread(t);
//启动线程
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}
个人小结
多线程的使用,便于用户开服更为复杂的程序。特别是同步方法的使用,更便于用户使用多线程。效率更高。