线程(一)
2021-04-10 02:28
标签:while you 方法 else 主线程 build 的区别 layout amp 线程安全:StringBuilder非线程 StringBuffer线程 / Vector线程 ArrayList非线程 / 快速迭代时不能有其他线程进行操作 进程:操作系统结构的基础:是一个正在执行的程序,计算机中正在运行的程序实例 线程:线程(thread)是进程中某个单一顺序的控制流,是程序运行的基本执行单元,任何一个程序中至少需要一个主线程 线程优缺点: 可以使用线程让多条业务逻辑同时处理成为可能! 线程同步:只有单一的线程,按照既定的程序流依次执行代码 线程异步:多个线程同时执行代码 线程的创建两种方式: 继承Thread类 :public class MyThread extends Thread{} 实现Runnable接口 :public class MyThread implements Runnable(){} 两种方法都要重写run方法,该方法是线程的核心方法,用于封装线程要处理的业务内容 run方法是线程类中最重要的方法,该线程中所有需要单独执行的业务逻辑都需在run方法中运行 两种方法的区别: 启用线程:使用start()方法启动线程 线性远程:run()方法不需要我们自己调用,当调用start()方法时将自动调用run方法,直到run方法执行完毕或人为停止或挂起 多线程的创建步骤: 建立多个类继承Thread类或实现Runnable接口 实现run方法 实例化线程类并调用start方法 如果要多个线程实现同样的功能: 线程的生命周期:线程和人一样也会生老病死经理各个阶段 主要经历如下阶段 获取线程的当前状态: getState() 获得线程的名称: getName() 控制线程的主要方法 yeild、 join、suspend、sleep、wait等方法的调用,都可是程序进入阻塞等待状态,若一致处于阻塞状态,则成为线程死锁 例子:编写两个线程,并在各自线程中打印1-10的数值,观察结果 通过继承获得的线程 通过实现接口获得的线程 测试类 例子:时间窗体实现长时间的走动 stop方法需要慎用,可能会引发多线程的数据安全问题,多线程使用过程中如果产生了并发访问(多个线程对同一个对象进行了访问:比如A线程往C集合中添加数据,同时B线程也要往C集合中添加数据),需要用到线程锁(对象锁) 比如:A线程要访问C对象进行数据处理并将该C对象进行了锁定,另一个线程B也要访问C对象,但是需要等待A线程完成计算后,根据计算的结果再接着处理。此时如果A线程执行了stop方法,该方法将会释放对象锁,B线程此时会提前访问到C对象,B线程接下来的数据处理可能会出现问题 stop方法再多线程开发过程中,如果没有涉及到并发处理的情形,是没有隐患问题的 suspend将线程挂起,线程会进去阻塞状态,被挂起的线程只能同构resume进行恢复,两者必须一起进行使用 线程(一) 标签:while you 方法 else 主线程 build 的区别 layout amp 原文地址:https://www.cnblogs.com/gfl-1112/p/12650002.html
优点:程序执行思路步骤清晰,步骤分明
缺点:一次只能做一件事情,必须做完意见事后才能继续做另一件事
使用线程的好处:异步处理,简化编程模型,提高CPU使用public class MyThread extends Thread{
public void run(){
//线程执行的内容
}
}
继承自thread类是得到真正的一个线程对象,可以通过start方法开启线程
实现runnable接口,仅仅是将线程中要实现逻辑进行了抽离,同时也便于多线程的并发处理。
1.写一个runnable中,封装至不同的线程中(这种方式更方便)
2.继承thread,多次new可以实现相同功能(这种方式不方便,但是线程内容不同的时候这种方法更方便)
准备就绪: new
运行时 : runnable //调用start后
阻塞等待: wait (blocked timed_waiting waiting)
终止完成: terminated
判断线程的状态: isAlive()
start() 启动线程
stop() 终止当前线程 (过时方法)
suspend() 挂起线程
resume() 继续挂起的线程
join() 等待线程执行完毕
yield() 暂缓线程
sleep() 线程等待
wait() 线程等待
notify() 唤醒线程
线程死锁:线程处于阻塞状态后无法恢复,该情形成为线程死锁(会导致程序无法继续进行,所以所有处于阻塞状态的线程最后都应恢复运行,恢复后继续进入运行时状态)/**
* 自定义线程,通过继承
*/
public class MyThread1 extends Thread{//继承中有很多方法
/**
* 构造方法
* 传入线程的名称
* @param threadName
*/
public MyThread1(String threadName){
super(threadName);
//构造方法中的状态是new,等待就绪
//获取线程的状态 new
System.out.println(this.getState());
//判断线程是否存活 false,只有调用了start之后才会为true
System.out.println(this.isAlive());
}
@Override
//都要对run方法进行重写
public void run() {
//获得线程的名称
System.out.println("当前线程:"+getName());
System.out.println(this.getState());
System.out.println(this.isAlive());
//循环打印
for(int i = 1; i ){
System.out.println("线程A:"+i);
}
}
}/**
* 使用Runable接口实现线程
*/
public class MyThread2 implements Runnable{//runnable中只有一个run方法
@Override
//都要对run方法进行重写
public void run() {
//进行循环打印
for(int i = 1; i ){
System.out.println("线程B:"+i);
}
}
}public class RunThead {
/**
* main方法的执行就会启动一条主线程
* 该主线程通常也将其称为守护线程或幽灵线程
* @param args
*/
public static void main(String[] args) {
System.out.println("程序开始运行");
//实例化继承了Thead线程
MyThread1 th1 = new MyThread1("线程A");
//开启线程,不能直接访问run方法,不然就不是开启一个线程
th1.start();
try {
//将线程加入线程队列,使线程阻塞
th1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//若在外部打印状态和是否存活,状态则不一定,因为主线程和其余线程是同时进行的,不一定哪个线程先结束
//但是加了th1.join()方法后线程阻塞了以后,则会先将线程走完,再进行主线程后面的代码,则会看到结束后的状态和不存活
System.out.println("main中的线程状态:"+th1.getState());
System.out.println("main中的线程是否存活:"+th1.isAlive());
//线程如果处于结束状态,无法再次启动,如果需要在启动,则需要重新创建
th1 = null;//赋值为null意思是这个线程结束以后要弃用了
th1 = new MyThread1("");//重新创建启用
th1.start();
//实例化实现了Runnable接口的实例
MyThread2 runnable = new MyThread2();
//如果实现接口后直接调用,则只能调用run方法,不能算一个线程
//th2.run();
//可以将Runnable实例封装至线程中,才是线程
Thread th2 = new Thread(runnable);
th2.start();
//由主线程进行运行
System.out.println("程序运行结束");
}
}/**
* 通过窗体实现时间的走动
*/
public class TimeFrame extends JFrame {
//都写成属性,等到用的时候再new出来
//标签,用于呈现文字
private JLabel lbl;
//开始按钮
private JButton btnStart;
//停止按钮
private JButton btnStop;
//挂起按钮
private JButton btnSuspend;
//恢复按钮
private JButton btnResume;
//时间线程
private TimeThread thead;
//构造方法
public TimeFrame(){
//设置窗体大小和位置
this.setBounds(600, 400, 600, 300);
//设置窗体的关闭策略,关闭窗体时程序同时关掉
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//设置窗体的布局
this.setLayout(null);
//初始化组件
initCompoent();
}
/**
* 初始化组件
*/
private void initCompoent(){
//初始化标签
lbl = new JLabel();
//再标签中添加数据
lbl.setText("这是一个标签,用于呈现文本");
//设置标签的大小和位置
lbl.setBounds(100,100,400,25);
//将标签添加至窗体
this.add(lbl);
//初始化按钮
btnStart = new JButton("start");
//设置按钮的大小和位置
btnStart.setBounds(100, 150, 100, 25);
//将按钮添加至窗体
this.add(btnStart);
//绑定按钮的监听器
btnStart.addActionListener(new ButtonStartListener());
//相同的操作设置按钮
btnStop = new JButton("stop");
btnStop.setBounds(200, 150, 120, 25);
this.add(btnStop);
//绑定按钮的监听器
btnStop.addActionListener(new ButtonStopListener());
//相同的操作设置按钮
btnSuspend = new JButton("suspend");
btnSuspend.setBounds(300, 150, 120, 25);
this.add(btnSuspend);
//绑定按钮的监听器
btnSuspend.addActionListener(new ButtonSuspendListener());
//相同的方法设置按钮
btnResume = new JButton("resume");
btnResume.setBounds(400, 150, 120, 25);
this.add(btnResume);
//绑定按钮的监听器
btnResume.addActionListener(new ButtonResumeListener());
}
/**
* start按钮监听器
*/
public class ButtonStartListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
//判断条件是为了按钮只能操作一次,多次重复点击是无用的
//线程已经开始且线程不存活了,则可以新建一个线程
//if(thead != null && !thead.isAlive()){
//
if(thead != null && thead.getState() == Thread.State.TERMINATED){
System.out.println("前一条线程已经结束,创建了新的线程");
//创建一条新的线程
thead = new TimeThread();
//启动线程
thead.start();
}
//第一次运行线程,也可以新建一个线程
else if(thead == null){
System.out.println("第一次创建新的线程");
//创建一条新的线程
thead = new TimeThread();
//启动线程
thead.start();
}
}
}
/**
* Stop按钮监听器
*/
public class ButtonStopListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
//如果线程已被创建并且处于运行时状态
if(thead != null && thead.isAlive()){
//使线程停止,提前进入销毁状态
thead.stop();
}
}
}
/**
* Suspend按钮监听器
*/
public class ButtonSuspendListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(thead.getState());
//如果线程处于运行时状态(不能用isAlive判断,因为阻塞的状态下也是alive)
if(thead != null && (thead.getState() == Thread.State.RUNNABLE || thead.getState() == Thread.State.TIMED_WAITING)){
//将线程挂起,线程进入阻塞状态;处于suspend阻塞下的线程只能通过resume方法恢复
System.out.println("线程挂起");
thead.suspend();
}
}
}
/**
* Resume按钮监听器
*/
public class ButtonResumeListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
//对suspend线程进行恢复
if(thead != null){
thead.resume();
}
}
}
/**
* 用于刷新时间的线程
*/
public class TimeThread extends Thread{
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void run() {
while(true){
//获取系统时间
Date date = new Date();
//将日期格式化并写入标签
lbl.setText(sdf.format(date));
try {
//使线程休眠,进入到阻塞的状态,当到达指定时间后,线程将自动恢复至运行时状态
//1000毫秒是1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
//实例化窗体
TimeFrame frame = new TimeFrame();
//显示窗体
frame.setVisible(true);
}
}
suspend方法可能会引起死锁,使用suspend方法调用的线程必须要通过resume方法才能恢复(图形的suspend可以通过异步调用resume,但是如果不是图形状态,再本线程中suspend后无法对自身调用resume方法,就会无限阻塞引起死锁。除非用main方法主线程进行恢复,主线程中通过调用resume)