一、概念
1. 进程与线程
a. 进程---就是正在进行的程序。
CPU在某一时间段只处理一个程序。 程序也是一个进程,当程序结束,进程消失。b. 线程---就是程序中的一个执行路径(控制单元)
线程在控制着进程的执行; 它是真正的执行者。进程与线程的关系:一个进程中至少包含有一个或多个线程。
得到当前线程名:System.out.println(Thread.currentThread().getName());
2. 多线程存在的意义
多线程可以让我们的程序部分可以产生同时运行的效果,各玩各的。提高效率是其次,主要是能让多段代码同时执行。3. 多线程的目的:
为了最大限度的利用CPU资源。4. 线程调度:
按照特定的机制为多个线程分配CPU的使用权。调度的模式有两种:
(1) 分时调度:所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间;
(2) 抢占式调度:根据线程的优先级别来获取CPU的使用权。
JVM的线程调度模式采用了抢占式模式。
二、线程创建方式一:继承Thread类
关键词:Thread、run()、start()
1. Thread类创建,分两种写法:
步骤:
第1步:定义类继承Thread类;
第2步:子类中重写Thread类中的run()方法;
第3步:调用线程的start()方法写法一:
子类:------------------------------------------------- public class Person extends Thread{ @overriad public void run(){ System.out.println("这是个线程"); } } 测试类:------------------------------------------------ Person ps=new Person(); //初始化线程 ps.start(); //启动线程并调用run方法
写法二:
测试类:------------------------------------------------ new Thread(){ @overriad public void run(){ System.out.println("这是个线程"); } }.start();
1.重写run方法的目的:将自定义的代码存储在run方法中,让线程运行(也就是将同时要运行的代码写在run()方法中)。
2. 通过对象.run()调用,不用start()方法调用也可以,但run()就变成主线程中的方法,与线程没关系。 因为线程没有开启,你只执行了调用。
3. run()方法中仅仅是封装多线程运行的代码,而start()则是开启多线程的钥匙。
4. start()方法是开启多线程,并执行run()方法。
5. 多线程的一个特性:随机性(谁抢到谁执行,至于执行多长,CPU说了算)package javaadvan_07; //QQ程序 public class Demo_02 { public static void main(String[] args) { //线程的核心:随机,灵活。 //获取当前程序中的main线程 for(int i=1;i<=1000;i++) { System.out.println("当前程序的main线程"+Thread.currentThread().getName()); } //当前程序还需要多个线程来执行运行 //实例化 MyThread mt = new MyThread(); //调用start方法来启动线程 mt.start(); //run方法与start方法的区别 // run方法只是用来封装多线程执行的步骤(代码实现) // start是用来开启线程的钥匙 MyThread mt = new MyThread(); //调用start方法来启动线程 //设置线程的优先级别 1 5 10 mt.setPriority(Thread.MIN_PRIORITY); mt.start(); MyThread mt2 = new MyThread(); //调用start方法来启动线程 mt2.start(); } }
三、线程的操作
1. 获取线程的名字Thread.currentThread().getName();
2. 设置线程优先级
(1)每个线程都有一个优先级;
(2)高优先级线程的执行优先于低优先级线程;
(3)每个线程都可以或不可以标记为一个守护程序。
(4)当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。
setDaemon(true)
标识将该线程设置为后台线程,或者叫守护线程,或者叫精灵线程例:对象.setDaemon(true)
setPriority()
设置的优先级一定要足够长时间才会有效果
例:对象.setPriority(Thread.MAX_PRIORITY); //设置最高级别10 对象.setPriority(Thread.MIN_PRIORITY); //最低级别1 对象.setPriority(Thread.NORM_PRIORITY); //值为5
注意:
(1)设置线程优先级的代码,要放在.start()前面。
(2)优先级高不一定代表该线程就先抢到执行权,理论是是可以,但实际情况不一样。
四、线程的状态
||===> 阻塞(临时状态) || (有执行资格,没执行权) || start() || sleep(time) 新建 ========> 运行 =============> 冻结(睡眠|等待) || wait()/notify() stop() || || 消亡(真死了)
1.新建:刚new出来在内存中
2.就绪:start表示该线程有资格抢CPU!
3.运行:抢到了CPU,会执行run方法
4.冻结:放弃了执行资格,醒了获得执行资格,但不一定马上执行。
4.死亡:执行完run方法
5.阻塞:调用了sleep方法或其他阻塞的方法执行资格与执行权
① 没执行资格的情况下,是冻结状态;
② 有执行资格的是临时状态;
③ 既有执行资格又有执行权的是运行状态。package javaadvan_07; /** * 下载图片的线程 * @author Administrator * */ public class ImgThread extends Thread{ private String imgPath; public ImgThread(String imgPath) { this.imgPath = imgPath; } @Override public void run() { try { WebImageLoader.imgLoad(imgPath); } catch (Exception e) { } } } package javaadvan_07; public class Demo_03 { public static void main(String[] args) { ImgThread it1 = new ImgThread("https://uploadfile.bizhizu.cn/up/7b/60/c7/7b60c7c967c06ab52bc9e2da0f4cbcbc.jpg"); it1.start(); ImgThread it2 = new ImgThread("https://pic.3gbizhi.com/2019/1009/20191009011724789.jpg"); it2.start(); ImgThread it3 = new ImgThread("https://uploadfile.bizhizu.cn/up/c8/1c/2f/c81c2fe4af5222ff81f81e8f74e2ac26.jpg"); it3.start(); } }
五、创建线程的第二种方式:实现Runnable接口
步骤:
1. 创建类实现Runnable接口;
2. 重写Runnable接口中的run()方法;
将线程要运行的代码存放在该run方法中。
3. 通过Thread类建立线程对象;
4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法;
5. 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。public class Ticket implement Runnable{ private int tick=100; @override public void run(){ while(true){ if(tick>0){ System.out.println(Thread.currentThread().getName()+" sale..."+ tick--); } } } public class TickDemo{ public static void main(String[] args){ Ticket t=new Ticket(); //先实例化一个卖票的对象 //实例化四个线程 Thread t1=new Thread(t); //创建线程 Thread t2=new Thread(t); Thread t3=new Thread(t); Thread t4=new Thread(t); //让四个线程开始工作 t1.start(); //执行线程启动run() t2.start(); t3.start(); t4.start(); } }
六、多线程的安全性
1. 问题:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句时执行了一部分,还没有执行完,另一个线程参与进来执行。导致了共享数据的错误。2. 解决办法:
对多条操作共享数据的语句,只能让一个线程执行完。在执行过程中,其他线程不可以参与执行。Java对于多线程的安全问题提供了专业的解决办法:同步。
同步分:同步代码块、同步方法锁旗标(同步):synchronized
(1) 同步代码块: synchronized(对象){ //需要被同步的代码 } //表示该段代码上锁 //如果是代码块后面要放上锁旗标,如果修饰方法,那么它的锁旗标是隐含的this。 (2)锁旗标也可以修饰方法----同步方法。
七、利用线程实现小游戏
老虎机小游戏:
package com.lixiangning.files_07; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class LFJGame extends JFrame{ ImageIcon iia=new ImageIcon("image\\1.png"); ImageIcon iib=new ImageIcon("image\\2.png"); ImageIcon iic=new ImageIcon("image\\3.png"); JLabel jla=new JLabel(iia); JLabel jlb=new JLabel(iib); JLabel jlc=new JLabel(iic); JLabel jld=new JLabel(iia); JLabel jle=new JLabel(iib); JLabel jlf=new JLabel(iic); JLabel jlg=new JLabel(iia); JLabel jlh=new JLabel(iib); JButton jba=new JButton("开始"); JButton jbb=new JButton("结束"); boolean flag=true; public LFJGame() { this.setTitle("老虎机"); this.setSize(420, 450); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); JPanel jpa=new JPanel(); jpa.setLayout(null); jla.setBounds(20, 20, 87, 87); jpa.add(jla); jlb.setBounds(150, 20, 87, 87); jpa.add(jlb); jlc.setBounds(280, 20, 87, 87); jpa.add(jlc); jld.setBounds(20, 150, 87, 87); jpa.add(jld); jle.setBounds(20, 290, 87, 87); jpa.add(jle); jlf.setBounds(150, 290, 87, 87); jpa.add(jlf); jlg.setBounds(280, 150, 87, 87); jpa.add(jlg); jlh.setBounds(280, 290, 87, 87); jpa.add(jlh); jba.setBounds(115, 170, 70, 30); jpa.add(jba); jbb.setBounds(195, 170, 70, 30); jpa.add(jbb); jba.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //1.点击变图 // int a1= (int) (Math.random()*6+1); // int a2= (int) (Math.random()*6+1); // int a3= (int) (Math.random()*6+1); // // iia=new ImageIcon("image\\"+a1+".png"); // iib=new ImageIcon("image\\"+a2+".png"); // iic=new ImageIcon("image\\"+a3+".png"); // // jla.setIcon(iia); // jlb.setIcon(iib); // jlc.setIcon(iic); //2.动起来 flag=true; new Thread() { public void run() { while (flag) { /*int a1= (int) (Math.random()*6+1); int a2= (int) (Math.random()*6+1); int a3= (int) (Math.random()*6+1); iia=new ImageIcon("image\\"+a1+".png"); iib=new ImageIcon("image\\"+a2+".png"); iic=new ImageIcon("image\\"+a3+".png"); jla.setIcon(iia); jlb.setIcon(iib); jlc.setIcon(iic);*/ //优化代码 ht(jla,iia); ht(jlb,iib); ht(jlc,iic); ht(jld,iia); ht(jle,iib); ht(jlf,iic); ht(jlg,iia); ht(jlh,iib); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; }.start(); } }); //结束 jbb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { flag=false; } }); this.getContentPane().add(jpa); this.setVisible(true); } //换图 public void ht(JLabel jl,ImageIcon ii) { int a1= (int) (Math.random()*6+1); ii=new ImageIcon("image\\"+a1+".png"); jl.setIcon(ii); } public static void main(String[] args) { new LFJGame(); } }
飞天猪小游戏:
package com.lixiangning.files_07; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class ZhuZhu extends JFrame{ private JPanel jpa = new JPanel(null); private ImageIcon image = new ImageIcon("image\\bj.gif"); private JLabel jla = new JLabel(image); private ImageIcon imb = new ImageIcon("image\\zz.gif"); private JLabel jlb = new JLabel(imb); private JLabel jlc = new JLabel(new ImageIcon("image\\zd.gif")); public ZhuZhu() { this.setTitle(""); this.setSize(820,320); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); jla.setBounds(0, 0,820, 320); jpa.add(jla); jlb.setBounds(670,160,96,96); jla.add(jlb); jlc.setBounds(680, 180, 78, 78); jla.add(jlc); this.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { char keyChar = e.getKeyChar(); if(keyChar == 'a') { jlb.setLocation(jlb.getX()-10, jlb.getY()); }else if(keyChar == 'd') { jlb.setLocation(jlb.getX()+10, jlb.getY()); }else if(keyChar == 'w') { jlb.setLocation(jlb.getX(), jlb.getY()-10); }else if(keyChar == 's') { jlb.setLocation(jlb.getX(), jlb.getY()+10); } } @Override public void keyReleased(KeyEvent e) { // System.out.println("按键释压"); } @Override public void keyPressed(KeyEvent e) { // System.out.println("按键获压"); } }); this.getContentPane().add(jpa); this.setVisible(true); } public static void main(String[] args) { new ZhuZhu(); } }
简单游戏:
package com.lixiangning.files_07; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; public class MyGame extends JFrame{ private JPanel jpa = new JPanel(null); private JLabel jla = new JLabel(new ImageIcon("image\\1.png")); private JLabel jlb = new JLabel(new ImageIcon("image\\2.png")); private JLabel jlc = new JLabel(new ImageIcon("image\\3.png")); boolean f = true; public MyGame() { this.setTitle("游戏机"); this.setSize(841, 350); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); jla.setBounds(630, 140, 78, 78); jlb.setBounds(20, 140, 78, 78); jlc.setBounds(650, 250, 78, 78); jpa.add(jla); jpa.add(jlb); jlc.setVisible(false); jpa.add(jlc); new Thread() { public void run() { f = true; while(f) { try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(jlb.getY()+128 <= 300) { // System.out.println(jlb.getY()); jlb.setLocation(jlb.getX(), jlb.getY()+5); }else { f = false; } } }; }.start(); if(f == false) { new Thread() { public void run() { while(true) { if(jlb.getY()+128 >= 300) { jlb.setLocation(jlb.getX(), jlb.getY()-5); } if(jlb.getY()+128 < 10) { f = true; break; } } }; }.start(); } this.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { char keyChar = e.getKeyChar(); if('A' == keyChar) { // System.out.println(keyChar); //子弹显示 jlc.setVisible(true); new Thread() { public void run() { while(true) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } jlc.setLocation(jlc.getX()-10, jlc.getY()); if(jlc.getX() == jlb.getX()) { JOptionPane.showMessageDialog(null, "GAME over"); break; } } }; }.start(); } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } }); this.getContentPane().add(jpa); this.setVisible(true); } public static void main(String[] args) { new MyGame(); } }
简单实现点名器:
package com.lixiangning.files_07; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class DianMingQi extends JFrame { private JPanel jpa = new JPanel(null); private JLabel jla = new JLabel("芝麻馅的汤圆0"); private JButton jba = new JButton("开始"); private JButton jbb = new JButton("终止"); private String [] mz = {"芝麻馅的汤圆1","芝麻馅的汤圆2","芝麻馅的汤圆3","芝麻馅的汤圆4","芝麻馅的汤圆5", "芝麻馅的汤圆6","芝麻馅的汤圆7","芝麻馅的汤圆8","芝麻馅的汤圆9"}; private boolean flag = true; private int timer = 50; public DianMingQi() { this.setTitle("点名器"); this.setSize(820, 350); this.setDefaultCloseOperation(3); this.setLocationRelativeTo(null); jla.setBounds(270, 30, 400, 100); jla.setFont(new Font("华文楷体", Font.BOLD, 45)); jpa.add(jla); jba.setBounds(290, 180, 100, 50); jba.setFont(new Font("华文楷体", Font.BOLD, 27)); jpa.add(jba); jbb.setBounds(430, 180, 100, 50); jbb.setFont(new Font("华文楷体", Font.BOLD, 27)); jpa.add(jbb); this.getContentPane().add(jpa); jba.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { flag = true; new Thread() { public void run() { while(flag) { try { Thread.sleep(200); } catch (Exception e2) { // TODO: handle exception } Random rd = new Random(); int sj = rd.nextInt(9); jla.setText(mz[sj]); timer+=5; if(timer >= 300) { timer = 50; break; } } }; }.start(); } }); jbb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub flag = false; } }); this.setVisible(true); } public static void main(String[] args) { new DianMingQi(); } }