Java多线程&多进程

概念

多进程概念
当前的操作系统都是多任务os
每个独立的任务称为一个进程
os将时间划分成多个时间片
每个时间片内将cpu分配给某一个任务,时间片结束,cpu将自动回收,再分配给另外任务。从外部看,所有任务是在同时执行。但是在cpu上,任务是按照串行依次运行(单核cpu)。如果是多核,多个进程任务可以并行。

多线程概念
一个子程序可以包括多个子任务,可串行/并行
每个子任务可以称为一个线程
如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作。这样CPU还是保留在本程序中,而不是被调度到别的程序(进程)中。这样,提高本程序所获得的CPU时间和利用率。

创建多线程

在这里插入图片描述在这里插入图片描述
注意:
1、调用thread类的run方法来启动run方法,将会是串行运行(调用开始后,该线程未结束前不会执行其他)
2、调用start方法来启动run方法,将会是并行运行(多线程运行)
3、实现Runnable接口的对象必须包装在Thread类中才可以启动
不能直接对Runnable的对象进行start方法
eg Thread3 implements Runnable{,}
及Thread3是一个实现了Runnable接口的类
Thread3 tt = new Thread3(); // 创建一个Thread3的实例
Thread t = new Thread(tt);
t.start(); // runnable对象必须放在Thread类中才会运行

在这里插入图片描述
及java都是单根继承,如果用Thread继承则其无法再继承其他类。且Thread类的本身就是实现了Runnable的接口。

例子

public class ThreadDemo {
    
    
	public static void main(String[] args) throws Exception {
    
    
		TestThread1 tt = new TestThread1();
		Thread t = new Thread(tt);
		t.start();
		while(true) {
    
    
			System.out.println("MainThread is running");
			Thread.sleep(1000);
		}
	}
}

class TestThread1 implements Runnable{
    
    
	public void run() {
    
    
		while(true) {
    
    
			System.out.println("TestThread1 is running");
				try {
    
    
					Thread.sleep(1000);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
}

在这里插入图片描述

多线程信息共享

通过共享变量达到信息共享
(1)static变量
(2)同一个Runnable类的成员变量


-------------Thread类的信息共享只能用static变量-------------------

结果 -卖出103张(有偏差)

public class ThreadDemo {
    
    
	public static void main(String[] args) throws Exception {
    
    
		new TestThread1().start();
		new TestThread1().start();
		new TestThread1().start();
		new TestThread1().start();
	}
}

class TestThread1 extends Thread{
    
    
	
	// private int tickets = 100;  	每个线程卖100张,没有共享
	private static int tickets = 100;  // 所有线程共享的变量 
	
	public void run() {
    
    
		while(true) {
    
    
			if(tickets > 0) {
    
    
				System.out.println(Thread.currentThread().getName() + "  is selling  "+ tickets);
				tickets--;
			} else {
    
    
				break;
			}
		}
	}
}

注意下面的
TestThread1对象只被创建了一次,就是t。即内存中只有一个tickets
而new Thread(t)并没有创建TestThread1对象
而是把t包装成线程对象,然后启动
4个Thread使用的是同一个TestThread1对象

注意区分 是同一个线程start多次会报错

-------------实现Runnable的信息共享-------------------

结果 -卖出103张(有偏差)

public class ThreadDemo {
    
    
	public static void main(String[] args) throws Exception {
    
    
		TestThread1 t = new TestThread1();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

class TestThread1 implements Runnable{
    
    
	

	private int tickets = 100;  
	
	public void run() {
    
    
		while(true) {
    
    
			if(tickets > 0) {
    
    
				System.out.println(Thread.currentThread().getName() + "  is selling  "+ tickets);
				tickets--;
			} else {
    
    
				break;
			}
		}
	}
}

出错原因:
在这里插入图片描述
上述关键步骤:tickets–
在工作缓存中修改了一个变量值,但其他线程的工作缓存并没有更新
解决:采用volatile关键字修饰变量
保证一个变量在工作缓存中修改了,其他线程能马上看到
(保证不同线程对共享变量操作时的可见性)

解决工作副本的可见性问题

再看一个例子

public class ThreadDemo {
    
    
	public static void main(String[] args) throws Exception {
    
    
		TestThread1 t = new TestThread1();
		t.start();
		Thread.sleep(2000);
		t.flag = false;
		System.out.println("Main Thread is exiting");
	}
}

class TestThread1 extends Thread{
    
    
	
	boolean flag = true; 
	// volatile boolean flag = true; //volatile修饰的变量可以及时在各个线程里通知
	public void run() {
    
    
		int i = 0;
		while(flag) {
    
    
			i++;
		}
		System.out.println("Test Thread is exiting");
	}
}


主线程输出了停止,但是子线程还在继续,说明子线程没看到false
即子线程用的是工作缓存中的flag,主存中的是false,工作缓存是true,而线程只依赖工作缓存
而volatile修饰的变量可以所有的线程都能立刻看到值的变化

关键步骤加锁限制在这里插入图片描述

synchronized可以修饰一个代码块也可以修饰一个函数
synchronized关键字作为对象锁

正确:

public class ThreadDemo {
    
    
	public static void main(String[] args) throws Exception {
    
    
		TestThread1 t = new TestThread1();
		new Thread(t, "Thread-0").start();
		new Thread(t, "Thread-1").start();
		new Thread(t, "Thread-2").start();
		new Thread(t, "Thread-3").start();
	}
}

class TestThread1 implements Runnable{
    
    
	
	private volatile int tickets = 100; // 所有线程都可看到变化  
	//String str = new String("");
	// 抢到str锁的可以进入
	public void run() {
    
    
		while(true) {
    
    
			//synchronized (str) {  // 同步代码块   一次只能有一个线程进入方法
				sale();     
			//}
			try {
    
    
				Thread.sleep(100);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(tickets <= 0) {
    
    
				break;
			}
		}
	}
	
	public synchronized void sale() {
    
      // 同步函数
		if(tickets > 0) {
    
    
			System.out.println(Thread.currentThread().getName() + "  is selling  "+ tickets);
			tickets--;
		}
	}
}

多线程生命周期管理

在这里插入图片描述在这里插入图片描述
start后线程在running和runnable状态中切换,runnable表示准备就绪等待运行

线程阻塞&唤醒
-sleep 时间一到自我唤醒 sleep后进入block状态
-wait/notify/notifyAll 等待,需要别人来唤醒(notify唤醒)
join等待另一个线程结束

典例:生产者与消费者问题
1、wait() 与 notify/notifyAll 方法必须在同步代码块中使用在这里插入图片描述
从改例子中可以看出,线程wait后需要其他线程notify,线程被动地暂停和终止,不能及时释放资源,很危险。

线程主动暂停和终止
**利用定期监测共享变量的方式
*如果需要暂停和终止,先释放资源,再主动动作

public class InterruptTest {
    
    
	public static void main(String[] args) throws InterruptedException {
    
    
		// TODO Auto-generated method stub
		TestThread1 t1 = new TestThread1();
		TestThread2 t2 = new TestThread2();
		
		t1.start();
		t2.start();
		
		// 让线程运行一会后中断
		Thread.sleep(2000);
		t1.interrupt();  // 被动中断
		t2.flag = false; // 利用共享变量主动中断
		System.out.println("main thread is exiting");
	}
}

class TestThread1 extends Thread{
    
    
	public void run(){
    
    
//		判断标志,当本线程被别人interrupt后,jvm会被本线程设置interrupted标记
		while(!interrupted()) {
    
    
			System.out.println("test thread1 is running");
			try {
    
    
				Thread.sleep(1000);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
				break;
			}
		}
		System.out.println("test thread1 is exiting");
	}
}

class TestThread2 extends Thread{
    
    
	public volatile boolean flag = true;
	public void run(){
    
    
		while(flag) {
    
    
			System.out.println("test thread2 is running");
			try {
    
    
				Thread.sleep(1000);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("test thread2 is exiting");
	}
}

在这里插入图片描述

多线程死锁(每个线程互相占有别人需要的锁)
典例:哲学家吃面问题
预防死锁:对资源进行等级排序

public class Deadlocks {
    
    
	public static Integer r1 = 1;
	public static Integer r2 = 2;
	
	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		TestThread1 t1 = new TestThread1();
		t1.start();
		TestThread2 t2 = new TestThread2();
		t2.start();
	}

}

class TestThread1 extends Thread{
    
    
	public void run() {
    
    
//		先要r1 再要r2
		synchronized (Deadlocks.r1) {
    
     // 加锁
			try {
    
    
				Thread.sleep(3);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (Deadlocks.r2) {
    
    
				System.out.println("TestThread1 is running");
			}
		}
	}
}

class TestThread2 extends Thread{
    
    
	public void run() {
    
    
//		先要r1 再要r2
		synchronized (Deadlocks.r2) {
    
     // 加锁
			try {
    
    
				Thread.sleep(3);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (Deadlocks.r1) {
    
    
				System.out.println("TestThread2 is running");
			}
		}
	}
}

结果:无输出
修改排序:要求两个都是先拿1再拿2
······················································································

守护线程
普通线程的结束,是run方法运行结束
守护线程的结束,是run方法运行结束,或main函数运行结束
守护线程永远不要访问资源,如文件或数据库,因为是被动地暂停和终止(随main函数),来不及释放资源

// 	守护线程
public class Daemonthread {
    
    

	public static void main(String[] args) throws InterruptedException {
    
    
		// TODO Auto-generated method stub
		TestThread1 t = new TestThread1();
		t.setDaemon(true); // 设置为守护线程
		t.start();
		Thread.sleep(1000);
		System.out.println("Main thread is exiting");
	}

}

class TestThread1 extends Thread{
    
    
	public void run() {
    
    
		while(true) {
    
    
			System.out.println("TestThread1 is running");
			try {
    
    
				Thread.sleep(1000);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}

主线程结束后,test线程不在不再继续在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44227389/article/details/107284125