多线程
DOS系统只支持单进程,一旦程序有问题,系统就不能运行其他程序,而Windows系统则支持了多进程,只是会消耗大量资源,程序执行性能下降。由此,又开发了出了多线程的概念,依附于指定的进程,又可以并发执行,快速启动。
例如,每次启动word都像开启了一个进程,而如拼写检查,联网服务都是一个个的线程。进程消失,线程肯定消失,但是线程消失,进程不一定消失
一、多线程的实现
Thread是线程的主要操作类
public class MyThreadDemo {
public static void main(String[] args) throws Exception{
new MyThread("线程1").start(); // 启动线程,这里才是真正的启动线程,调用系统的start0方法
new MyThread("线程2").start();
new MyThread("线程3").start();
}
}
class MyThread extends Thread{
private String title;
public MyThread(String title){
this.title = title;
}
@Override
public void run(){ //覆写run方法,这里提供的是资源的准备
for (int x=0;x<10;x++){
System.out.println(this.title+" x= "+x);
}
}
}
二、Runnable接口
Thread类只能单继承,局限性太大,因此开发了Runnable接口,定义如下
@FunctionalInterface
public interface Runnable{
public void run();
}
1. Runnable实现
这样通过集成Runnable就能实现run方法,并且这个接口还支持lambda函数式表达式
public class MyThreadDemo {
public static void main(String[] args) throws Exception{
new Thread(new MyThread("线程1")).start(); // 此时MyThread不是Thread的子类,因此需要new一个
new Thread(new MyThread("线程2")).start();
new Thread(new MyThread("线程3")).start();
}
}
class MyThread implements Runnable{
private String title;
public MyThread(String title){
this.title = title;
}
@Override
public void run(){
for (int x=0;x<10;x++){
System.out.println(this.title+" x= "+x);
}
}
}
2. lambda表达式实现
还能通过lambda表达式的方法,直接定义线程方法主体
public class MyThreadDemo {
public static void main(String[] args) throws Exception {
for (int x= 0;x<3;x++){
String title = "线程对象-"+x;
new Thread(()->{ // lambda函数实现run方法,直接生成三个线程对象和线程竞争资源
for (int y=0;y<10;y++){
System.out.println(title+" y= "+y);
}
}).start();
}
}
}
3. Thread和Runnable的不同
实际开发中要使用Runnable避免单继承局限
public class Thread extents Objext implements Runnable{}
Thread类继承了Runnable接口,实际实现的还是Runnable中的run方法
此时Thread相当于一个Runnable的代理类,负责对接真实业务MyThread,直接进行内部转化资源调度
同时,Thread相当于new一个线程对象,而MyThread集成Runnable相当于new了一个线程资源,然后再把资源放到对象里,这样更加符合设计理念
public class MyThreadDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
class MyThread implements Runnable{ // 生成了一个卖票资源,让线程来争抢
int ticket = 5;
@Override
public void run(){
for (int x=0;x<100;x++){
if(this.ticket > 0){
System.out.println("剩余票数:"+this.ticket--);
}
}
}
}
4. Callable有返回接口
定义如下
@FunctionalInterface
public interface Callable<V>{ // 定义一个泛型,接受返回值
public V call() throws Exception{};
}
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyThreadDemo {
public static void main(String[] args) throws Exception{
FutureTask<String> task = new FutureTask<>(new MyThread()); // 包装Callable
new Thread(task).start(); // 把task包装起来,FutrueTask是一个过渡类
System.out.println("返回值: "+task.get());
}
}
class MyThread implements Callable<String>{
@Override
public String call() throws Exception {
for (int x=0;x<100;x++){
System.out.println("线程: x="+x);
}
return "线程返回";
}
}
5. 线程运行状态
- 创建状态:new Thread后,线程的对象和空间就已经分配好了,但是不可运行
- 就绪状态:调用start方法就可以启动线程,此时等待CPU调度服务
- 运行状态:获得处理器资源后,开始调用该线程的对象的run方法
- 阻塞状态:认为挂起,或等待某项任务执行,就会让CPU暂时中止自己的行为,进入阻塞状态
- 中止状态:run结束后,终止状态,线程不再运行
三、多线程常用操作方法
1. 获取线程名字
public class MyThreadDemo {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt,"线程a").start();
new Thread(mt).start();
new Thread(mt,"线程b").start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName());
}
}
}
主线程获取
public class MyThreadDemo {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt,"线程a").start();
mt.run(); // 直接调用实例化对象的run方法
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName());
}
}
}
进程是由多个线程构成的,使用java命令执行一个类的时候,就已经启动了一个JVM进程,主方法则是这个进程上的一个线程而已,类执行完毕后,线程就消失了
实际开发过程中,所有的线程都是通过主线程来创建的
利用子线程,处理一下复杂的逻辑的同时,不影响别的任务的执行,如下
public class MyThreadDemo {
public static void main(String[] args) throws Exception{
new Thread(()->{
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
}).start(); // 耗时任务
System.out.println("任务A");// 简单任务不需要等待耗时任务完成后才能执行
System.out.println("任务B");
}
}
2. 线程休眠
No. | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public static void sleep(long millis) throws InterruptedException | 普通 | 设置线程休眠的毫秒数,时间一到自动唤醒 |
2 | public static void sleep(long millis, int nanos) throws InterruptedException | 普通 | 设置毫秒数和纳秒数 |
public class MyThreadDemo{
public static void main(String[] args) {
Runnable run = ()->{
for (int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName()+"、x= "+x);
try {
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
}
};
for (int i=0;i<5;i++){
new Thread(run,"线程对象-"+i).start();
}
}
}
3. 线程中断
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
Thread thread = new Thread(()->{
System.out.println("准备睡觉");
try {
Thread.sleep(10000);
System.out.println("正在睡觉,美滋滋!");
} catch (InterruptedException e){
System.out.println("被吵醒了,生气!");
}
});
thread.start();
Thread.sleep(1000); // 子进程先运行一秒
if (!thread.isInterrupted()){ // 打断进程
System.out.println("一声炮响");
thread.interrupt();
}
}
}
4. 线程强制执行
5. 线程礼让
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
Runnable run = ()->{
for(int i=0;i<100;i++){
if (i%3==0){
Thread.yield();
System.out.println("太君,这边请!"+ Thread.currentThread().getName());
}
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" i= "+i);
}
};
new Thread(run,"A").start();
for (int x=0;x<100;x++){
Thread.sleep(100);
System.out.println("呦西,你滴大大滴良民!");
}
}
}
6. 线程优先级
No. | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public static final int MAX_PRIORITY | 常量 | 最高优先级,10 |
2 | public static final int NORM_PRIORITY | 常量 | 中等优先级,5 |
3 | public static final int MIN_PRIORITY | 常量 | 中等优先级,5 |
4 | public final void setPriority(int newPriority) | 普通 | 设置线程优先级 |
5 | public final int getPriority() | 普通 | 取得线程优先级 |
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
Runnable run = ()->{
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"i= "+i);
}
};
Thread threadA = new Thread(run,"A");
Thread threadB = new Thread(run,"B");
Thread threadC = new Thread(run,"C");
threadA.setPriority(Thread.MAX_PRIORITY);
threadC.setPriority(Thread.MIN_PRIORITY);
System.out.println(threadB.getPriority());
threadA.start();
threadB.start();
threadC.start();
}
}
四、线程同步与死锁
现在有三个线程,每个线程中都运行着一个run方法,调用start方法开始运行。例如下面的卖票程序
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
class MyThread implements Runnable{
private int ticket = 3;
@Override
public void run() {
while (true){
if (this.ticket>0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("余票 = "+this.ticket--);
} else {
System.out.println("无票");
break;
}
}
}
}
// 输出
//余票 = 2
//余票 = 3
//余票 = 3
//余票 = 1
//无票
//余票 = -1
//无票
//余票 = 0
//无票
每个线程中都在执行run方法中的程序,并且通过共同的变量ticket来判断是否执行,但是当判断ticket大于0到ticket–的中间,需要执行时间,这就造成了线程的不同步,可能第一个线程得知余票=3,开始执行程序,但是尚未输出余票减一,第二线程此时也得知了余票=3,开始执行,这就造成了不同步。当第一个程序运行出来时,ticket=2,第二个程序
1. 同步代码块
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt, "A").start();
new Thread(mt, "B").start();
new Thread(mt, "C").start();
}
}
class MyThread implements Runnable{
private int ticket = 3;
@Override
public void run() {
while (true){
synchronized (this){
if (this.ticket>0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"余票 = "+this.ticket--);
} else {
System.out.println("无票");
break;
}
}
}
}
}
2. 同步方法
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt, "A").start();
new Thread(mt,"B").start();
new Thread(mt,"C").start();
}
}
class MyThread implements Runnable{
private int ticket = 3;
@Override
public void run() {
while (this.sale()){}
}
public synchronized boolean sale(){
if (this.ticket>0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"余票 = "+this.ticket--);
return true;
} else {
System.out.println("无票");
return false;
}
}
}
3. 线程死锁
4. 生产者和消费者
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
Message msg= new Message();
new Thread(new Producer(msg)).start();
new Thread(new Consumer(msg)).start();
}
}
class Producer implements Runnable{
private Message msg = null;
public Producer(Message msg){
this.msg = msg;
}
public void run(){
for (int x=0;x<50;x++){
if (x%2==0){
msg.setTitle("煎饼果子");
try {
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
msg.setContent("加个鸡蛋");
} else{
msg.setTitle("烙馍卷菜");
try {
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
msg.setContent("不要鸡蛋");
}
}
}
}
class Consumer implements Runnable{
private Message msg = null;
public Consumer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for (int x=0;x<50;x++){
try {
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(msg.getTitle()+" "+msg.getContent());
}
}
}
class Message{
private String title;
private String content;
public void setTitle(String title){
this.title = title;
}
public void setContent(String content){
this.content = content;
}
public String getTitle(){
return title;
}
public String getContent(){
return content;
}
}
线程同步
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
Message msg= new Message();
new Thread(new Producer(msg)).start();
new Thread(new Consumer(msg)).start();
}
}
class Producer implements Runnable{
private Message msg = null;
public Producer(Message msg){
this.msg = msg;
}
public void run(){
for (int x=0;x<50;x++){
if (x%2==0){
msg.set("煎饼果子","加个鸡蛋");
} else{
msg.set("烙馍卷菜", "不要鸡蛋");
}
}
}
}
class Consumer implements Runnable{
private Message msg = null;
public Consumer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for (int x=0;x<50;x++){
try {
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(msg.get());
}
}
}
class Message{
private String title;
private String content;
public synchronized void set(String title, String content){
this.title= title;
try {
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
this.content = content;
}
public synchronized String get(){
return this.title+"--->"+this.content;
}
}
五、Object线程等待与唤醒
No. | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public final void wait() throws InterruptedException | 普通 | 线程等待 |
2 | public final void notify() | 普通 | 按照等待的次序唤醒一个进程 |
3 | public final void notifyAll() | 普通 | 唤醒全部进程,并按优先级启动 |
六、优雅的停止线程
public class MyThreadDemo{
private static boolean flag = true;
public static void main(String[] args) throws Exception{
new Thread(()->{
long num=0;
while (flag){
try {
Thread.sleep(50);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" num="+num++);
}
},"执行线程").start();
Thread.sleep(300); // 300毫秒之后,flag变为false,线程中获取到,终止while循环
flag = false;
System.out.println("终止线程");
}
}
七、后台守护线程
public class MyThreadDemo{
private static boolean flag = true;
public static void main(String[] args) throws Exception{
Thread UserThread = new Thread(()->{
long num=0;
while (flag){
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" num="+num++);
}
},"亲爱的");
Thread DaemonThread = new Thread(()->{
for (int i=0;i<Integer.MAX_VALUE;i++){
try {
Thread.sleep(50);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" n= "+ i);
}
}, "守护者");
DaemonThread.setDaemon(true);
UserThread.start();
DaemonThread.start();
Thread.sleep(3000);
flag = false;
System.out.println("终止线程");
}
}
八、volatile关键字
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt, "A").start();
new Thread(mt, "B").start();
new Thread(mt, "C").start();
}
}
class MyThread implements Runnable{
private volatile int ticket = 3;
@Override
public void run() {
while (true){
synchronized (this){
if (this.ticket>0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"余票 = "+this.ticket--);
} else {
System.out.println("无票");
break;
}
}
}
}
}
volatile关键字是一种直接对内存的处理,主要在属性上使用,进程直接修改原始变量,而不是进行副本操作,synchronized是实现同步操作的关键字。
九、总结
- 线程是指程序的运行流程,多线程机制可以同时运行多个代码块,提高效率
- 实现多线程必须要继承Thread类或者实现Runnable接口,并且必须覆写run方法
- 每一个线程都会经历以下五种状态:创建、就绪、运行、阻塞、终止
- 可以使用synchronized关键字进行资源的同步处理,同时应防范死锁的产生
- Object类中提供有wait方法和唤醒方法notify(), notifyAll()
- 线程开发分为两种,用户线程和守护线程,守护线程依附于用户线程存在
- volatile关键字是对原始变量的定义,可以直接访问变量内存修改,而不是进行副本操作和数据同步
十、习题
问题抢答
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyThreadDemo{
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
FutureTask<String> taskA = new FutureTask<>(mt);
FutureTask<String> taskB = new FutureTask<>(mt);
FutureTask<String> taskC = new FutureTask<>(mt);
new Thread(taskA,"A").start();
new Thread(taskB,"B").start();
new Thread(taskC,"C").start();
System.out.println(taskA.get());
System.out.println(taskB.get());
System.out.println(taskC.get());
}
}
class MyThread implements Callable<String> {
private boolean flag = true;
@Override
public String call() {
synchronized (this){
if (this.flag){
this.flag=false;
return Thread.currentThread().getName()+" 抢答成功";
} else{
return Thread.currentThread().getName()+" 抢答失败";
}
}
}
}