所谓的多线程即在同一时间,可以做多件事情。
创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类
首先要理解进程(Processor)和线程(Thread)的区别
进程: 启动一个LOL.exe就叫一个进程。 接着又启动一个DOTA.exe,这叫两个进程。
线程: 线程是在进程内部同时做的事情,比如在LOL里,有很多事情要同时做,比如"盖伦” 击杀“提莫”,同时“赏金猎人”又在击杀“盲僧”,这就是由多线程来实现的。
一、创建多线程
1、继承线程类的方法创建
package com.baidu.demo.test11;
//继承线程类
public class ExtendsThread extends Thread {
// 重写run方法 run方法里面的代码就是要进行线程执行的代码
@Override
public void run() {
for(int i = 0;i < 10;i++){
//Thread.currentThread获取当前代码段所在线程的引用。 .getName就是获取线程的名字
System.out.println(Thread.currentThread().getName()+ "--->" + i);
}
}
}
2.实现Runnable接口创建多线程
package com.baidu.demo.test11;
//实现Runnalbe接口
public class ImplementsThread implements Runnable{
// 重写run方法 run方法里面的代码就是要进行线程执行的代码
@Override
public void run() {
for(int i = 0;i < 10;i++) {
//Thread.currentThread获取当前代码段所在线程的引用。 .getName就是获取线程的名字
System.out.println(Thread.currentThread().getName() + "--->" +i);
}
}
}
3.匿名类创建多线程
创建匿名多线程只需要直接new一个Thread就可以重写run方法
这个我直接写到了下面的主方法里面了
//匿名类创建多线程
Thread thread2 = new Thread() {
// 重写run方法 run方法里面的代码就是要进行线程执行的代码
@Override
public void run() {
for(int i = 0;i < 10;i++) {
System.out.println(Thread.currentThread().getName() + "--->" +i);
}
}
};
线程的开启
这里我开启了上面创建的三个线程
代码如下
package com.baidu.demo.test11;
public class Demo1 {
public static void main(String[] args) {
// 继承线程类的方法创建多线程
ExtendsThread extendsThread = new ExtendsThread();
//修改线程名称
extendsThread.setName("线程1");
//开启线程
extendsThread.start();
//实现Runnable接口创建多线程
Thread thread = new Thread(new ImplementsThread());
thread.setName("线程2");
thread.start();
//匿名类创建多线程
Thread thread2 = new Thread() {
// 重写run方法 run方法里面的代码就是要进行线程执行的代码
@Override
public void run() {
for(int i = 0;i < 10;i++) {
System.out.println(Thread.currentThread().getName() + "--->" +i);
}
}
};
thread2.setName("线程3");
thread2.start();
}
}
运行结果:
每次运行的结果都不一样哦 这就是最基本的多线程
二、常见线程方法
这个直接代码演示了吧
知识点在注释里 大家认真看
package com.baidu.demo.test11;
public class Demo2 {
public static void main(String[] args) {
//使用匿名类的方法创建多线程
Thread thread = new Thread() {
//重写run run里面的代码就是需要进行多线程执行的代码
@Override
public void run() {
for(int i = 0;i < 10;i++ ) {
//Thread.sleep是暂停当前线程的意思 1000表示的是1000毫秒 也就是一秒
try {
Thread.sleep(1000);
System.out.println("等待一秒后执行");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->" +i);
}
}
};
thread.setName("x1");
thread.start();
Thread thread2 = new Thread() {
// 重写run run里面的代码就是需要进行多线程执行的代码
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//线程.join表示的是把线程加入到当前线程中 这里表示当i=3时thread线程会加入进来 加入进来后会先执行完thread线程才会执行thread2线程
if(i == 3) {
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
};
//设置线程的优先级 Thread.MAX_PRIORITY说明该线程是最高优先级 也就是说cpu会先尽量优先运行该线程 这里处理的数据少所以可能看不出来效果 你们自己可以下来加大数据试一下 Thread.MIN_PRIORITY说明是最低优先级
thread2.setPriority(Thread.MAX_PRIORITY);
thread2.setName("x2");
thread2.start();
Thread thread3 = new Thread() {
// 重写run run里面的代码就是需要进行多线程执行的代码
@Override
public void run() {
//临时暂停该线程,使得其它线程可以占用CPU资源 这里处理的数据少所以可能看不出来效果 你们自己可以下来加大数据试一下
Thread.yield();
for (int i = 0; i < 10; i++) {
//线程.join表示的是把线程加入到当前线程中 这里表示当i=3时thread线程会加入进来 加入进来后会先执行完thread线程才会执行thread2线程
if(i == 3) {
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
System.out.println("守护线程结束");
}
};
/* 守护线程的概念是: 当一个进程里,所有的线程都是守护线程的时候,结束当前进程。
* 就好像一个公司有销售部,生产部这些和业务挂钩的部门。
* 除此之外,还有后勤,行政等这些支持部门。如果一家公司销售部,生产部都解散了,那么只剩下后勤和行政,那么这家公司也可以解散了。
* 守护线程就相当于那些支持部门,如果一个进程只剩下守护线程,那么进程就会自动结束。守护线程通常会被用来做日志,性能统计等工作。
*/
//设置成守护线程
thread3.setDaemon(true);
thread3.setPriority(Thread.MIN_PRIORITY);
thread3.setName("x3");
thread3.start();
}
}
运行结果:
这个的运行除了join加入线程、sleep暂停线程、setDaemon设置守护线程外 其它的每次运行都是不一样的
大家认真看代码的注释哦 自己也复制代码运行试一下 因为这个确实不太好演示 所以你们可能不太好理解
没事 多看几遍就懂了 我也是这么过来的 实在不懂的可以来问我
三、多线程的同步问题
多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题
多线程的问题,又叫Concurrency 问题
首先,我们来进行一个同步问题的演示:
代码如下:
package com.baidu.demo.test11;
public class Demo3 {
//声明一个int变量并赋值10000
public static int n = 10000;
public static void main(String[] args) {
//循环运行10000遍,每遍都创建两个线程,一个线程是给n加1,另一个线程是给n减1
//理论上加一次再减一次,最后输出还是n的值10000(事实并非如此)
//可能最后输出是10000,但是多运行几次,你会发现这个值不一定的
for(int i = 0;i < n ;i++) {
//第一个线程,用来给n加1
Thread t = new Thread() {
@Override
public void run() {
n++;
}
};
t.start();
//第二个线程,用来给n减1
Thread t1 = new Thread() {
@Override
public void run() {
n--;
}
};
t1.start();
//输出每循环一次后的值
System.out.println(n);
}
//输出最终结果
System.out.println("最后n的结果为:"+n);
}
}
运行结果:
分析同步问题产生的原因:
1、假设增加线程先进入,得到的hp是10000
2、进行增加运算
3.、正在做增加运算的时候,还没有来得及修改hp的值,减少线程来了
4.、减少线程得到的hp的值也是10000
5.、减少线程进行减少运算
6.、增加线程运算结束,得到值10001,并把这个值赋予n
7.、减少线程也运算结束,得到值9999,并把这个值赋予n
n,最后的值就是9999
虽然经历了两个线程各自增减了一次,本来期望还是原值10000,但是却得到了一个9999
这个时候的值9999是一个错误的值,在业务上又叫做脏数据
解决思路:
总体解决思路是: 在增加线程访问hp期间,其他线程不可以访问hp
1.、增加线程获取到n的值,并进行运算
2.、在运算期间,减少线程试图来获取n的值,但是不被允许
3.、增加线程运算结束,并成功修改n的值为10001
4.、减少线程,在增加线程做完后,才能访问n的值,即10001
5.、减少线程运算,并得到新的值10000
解决上述问题就需要用到synchronized关键字
使用方法为(其实所谓的线程安全的类就是用了该关键字):
Object someObject =new Object();
synchronized (someObject){
//此处的代码只有占有了someObject后才可以执行
}
解决上述同步问题的代码如下:
package com.baidu.demo.test11;
public class Demo4 {
//声明一个int变量并赋值10000
public static int n = 10000;
public static void main(String[] args) {
//声明一个对象
Object someObject = new Object();
//循环运行10000遍,每遍都创建两个线程,一个线程是给n加1,另一个线程是给n减1
for(int i = 0;i < n ;i++) {
//第一个线程,用来给n加1
Thread t = new Thread() {
@Override
public void run() {
//相当于占用了someObject
synchronized (someObject) {
//占用后才执行该代码
n++;
}
}
};
t.start();
//第二个线程,用来给n减1
Thread t1 = new Thread() {
@Override
public void run() {
//相当于占用了someObject
synchronized (someObject) {
//占用后才执行该代码
n--;
}
}
};
t1.start();
//输出每循环一次后的值
System.out.println(n);
}
//输出最终结果
System.out.println("最后n的结果为:"+n);
}
}
运行结果:
好啦,就这样吧
不讲太深了,怕你们一下子接受不了,想学更多的可以直接联系我QQ497099936
24小时不定时在线解答问题
同时,也欢迎撩骚
b( ̄▽ ̄)d
好啦,顾得白