Java第十七章-多线程

线程简介

//程序可以执行多个线程,,每个线程完成一个功能,并与其它线程并发执行,称为多线程。
//以多线程在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();

}
}

个人小结

多线程的使用,便于用户开服更为复杂的程序。特别是同步方法的使用,更便于用户使用多线程。效率更高。	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值