一.什么是线程?
-单线程
一个线程只能做一件任务
-多线程
多线程可以同时做多个任务
很多多线程是模拟出来的,真正的多线程指有多个cpu,即多核,如服务器
而在只有一个cpu模拟出来的多线程中,在同一时间点,cpu只能执行一个代码,因为切换的快,所以产生了同时执行的错觉
以人脑为例,在人看似边吃饭边玩手机时,由于人脑只有一个,在极短的瞬间中,我们其实只是在做吃饭和玩手机中的一件事
即使自己不创建线程,后台也有多个线程,如main线程、gc线程(java中基本所有程序都是多线程的)
-进程与线程
进程是执行一次程序的过程,一个进程包含多个线程
以美剧为例,想要看美剧,必须有声音、图像、字幕等,它们分别对应有自己的线程,同时运行这几个线程,完成了美剧播放这个进程。
二.实现线程
1)继承:Thread类
public class ThreadDemo1 extends Thread{
//重写run方法
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("任务1正在执行..."+i);
}
}
}
2)接口实现:Runnable接口
public class RunnableDemo1 implements Runnable{
//重写run方法
public void run() {
for(int i=0;i<50;i++){
System.out.println("任务2正在执行..."+i);
}
}
}
都需要重写run方法
run方法的作用:把需要同时运行的代码放入run方法中
三.启动线程
需要使用Thread类的start方法
不要调用成run方法
那么run方法就会被当成是普通方法被使用(即变成了以main线程,单线程实现程序)
public class ThreadTest {
public static void main(String[]args){
ThreadDemo1 td1=new ThreadDemo1();
//ThreadDemo1继承了Thread,其对象可以直接调用Thread类的start方法
td1.start();
RunnableDemo1 rd1=new RunnableDemo1();
//RunnableDemo1没有继承Thread类,不可直接调用start方法
//需要先创建Thread类的对象,在启动线程
Thread t2=new Thread(rd1);
//构造方法的参数是调用其run方法的对象
t2.start();
}}
启动这两个线程后,main线程就结束了(代码仍旧在跑)
线程如何结束?
等run方法执行完成
四.线程游戏实现
游戏效果
点击屏幕,创建一个小矩形,加入动态运行中
1)创建窗体(添加鼠标监听)
public class GameUI extends JFrame {
TongueListener tl=new TongueListener();
public GameUI(){
setTitle("快乐小狗");
setSize(1000,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
addMouseListener(tl);
tl.g=getGraphics();
}
public static void main(String[]args){
new HappyDogv1.GameUI();
}
2)小矩形运行的线程
public class DrawTongue extends Thread{
int x,y,speedx;
Graphics g;
public DrawTongue(int x, int y, int speedx, Graphics g) {
this.x = x;
this.y = y;
this.speedx = speedx;
this.g = g;
}
@Override
public void run() {
for(;;) {
x+=speedx;
g.setColor(Color.red);
g.fillRect(x, y, 15, 5);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
g.setColor(new Color(238,238,238));
g.fillRect(x,y,15,5);
//擦除矩形之前的轨迹
}
}
}
3)点击一个地方,多一个动态矩形(即点击一个地方启动一个线程)
监听器继承MouseAdapter就可以只重写需要需要的方法
public class TongueListener extends MouseAdapter {
Graphics g;
@Override
public void mousePressed(MouseEvent e) {
int x=e.getX();
int y=e.getY();
Random ran=new Random();
int speedx= ran.nextInt(5)+1;
DrawTongue dt=new DrawTongue(x,y,speedx,g);
dt.start();
}
在画动态矩形时暴露出来的问题
出现有残影,矩形重叠在一起时闪烁较为严重
happydogv1存在的问题
在消除轨迹时,采用画一整块与窗体颜色相同的白板,可以不留下残影
但是由于每加一个小矩形,就是一个新的线程,在虚拟的多线程中,短时的不断切换,使画笔议会儿红一会儿白发生错乱,且闪烁更加严重
happydog1多线程问题
这个时候减少线程数量就显得尤为重要
4)创建一个全局队列
代码实现
1>窗体类
public class GameUI extends JFrame {
GameUI(){
setTitle("快乐小狗");
setSize(1000,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
//搞一个队列的全局共享变量,它的作用就是用来存储要画在界面上的舌头的相关数据
ArrayList<Tongue> al=new ArrayList<Tongue>();
TongueListener tl=new TongueListener(getGraphics(),al);
addMouseListener(tl);
DrawThread dt=new DrawThread(getGraphics(),al);
dt.start();
}
public static void main(String[]args){
new GameUI();
}
}
2>监听类
public class TongueListener extends MouseAdapter {
private Graphics g;
private ArrayList<Tongue> al;
TongueListener(Graphics g,ArrayList<Tongue> al){
this.g=g;
this.al=al;
//与画笔类似,窗体和监听共用一个队列
}
@Override
//用click会导致有时候点不出来
public void mousePressed(MouseEvent e) {
int x=e.getX();
int y=e.getY();
Tongue t=new Tongue();
t.x=x;
t.y=y;
al.add(t);
}
}
3>线程类
public class DrawThread extends Thread{
private Graphics g;
private ArrayList<Tongue> al;
int speedx;
DrawThread(Graphics g,ArrayList<Tongue> al){
this.g=g;
this.al=al;
}
@Override
public void run() {
//run方法的作用,每间隔100ms,把队列中的舌头取出,在窗体上画一下
for(;;){
g.setColor(new Color(238,238,238));
g.fillRect(0,0,1000,800);
for(int i=0;i<al.size();i++){
Tongue t= al.get(i);
Random ran=new Random();
speedx=ran.nextInt(10);
t.x+=speedx;
g.setColor(Color.red);
g.fillRect(t.x,t.y,15,5);
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
程序效果
happdogv2优化效果
五.进一步实现的效果
1.在右方加入绿色小球,相向运动
加入目标小球
2.用图片代替红色小矩形
用图片替换矩形