【多线程】是啥?

什么是多线程?

可以简单这么理解:单线程的程序就像只雇佣一个服务员的餐厅,他必须做完一件事情后才可以做下一件事情;而多线程的程序则如同雇佣多个服务员的餐厅,他们可以同时做多件事情。
在这里插入图片描述
程序:一段静态的代码
进程:静态的代码的动态执行过程,产生,执行,消亡
线程:进程内最小的执行单位,动态执行的过程
进程有自己独立的内存
线程共享进程的资源,在进程内,每个线程可以完成独立的功能

线程创建的两种方式
1.继承 Thread 类: 线程

class T extends Thread{
	//线程完成的功能代码要写到线程体中
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("hello");
		}
	}
}
class T2 extends Thread{
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("thread");
		}
	}
}
public class MyThread {
	//main() 主线程,可以管理其他线程
	public static void main(String[] args) {
			T t1=new T();
			T2 t2=new T2();
			//启动线程,start方法
			t1.start();
			t2.start();

	}
}

2.实现 Runnable 接口(是一个函数式接口):不是线程

class Runnable1 implements Runnable{
	@Override
	//注意:run() 没有异常抛出,参数列表为空
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(i);
		}
	}
}
public class Demo {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable1 r=new Runnable1();
		Thread t=new Thread(r);
		t.start();
		//匿名函数的形式
		Thread t2=new Thread(()->{
			for(int i=0;i<10;i++) {
				System.out.println("hello");
			}
		}
				);
		t2.start();
	}

}

练习:用两种方式创建两个线程,打印10以内的偶数

class T3 extends Thread{
	public void run() {
		for(int i=0;i<=10;i++) {
			if(i%2==0) {
				System.out.println(i);
			}
		}
	}
}
public class MyThread {
	public static void main(String[] args) {
			T3 t3=new T3();
			t3.start();
	}
}
class Runnable2 implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<=10;i++) {
			if(i%2==0) {
				System.out.println(i);
			}
		}
	}
}
public class Demo {
	public static void main(String[] args) {
        Runnable2 r2=new Runnable2();
		Thread t3=new Thread(r2);
		t3.start();	
	}
}

线程类的构造方法
Thread()
//name - 新线程的名称。
Thread(String name)
Thread(Runnable r)
Thread(Runnable r,String name)

Thread t=new Thread() {
		//new Thread() {
			public void run() {
				System.out.println(Thread.currentThread());
				//继承Thread类,可使用方法
				System.out.println(getName());
				System.out.println(getId());
				System.out.println(isAlive());
			}
		//}.start();
		};
		t.setName("线程1");
		t.start();

结果为:

Thread[线程1,5,main]
线程1
13
true

常用方法

public static void main(String[] args) {
                //返回正在运行的线程对象
                Thread t=Thread.currentThread();
				System.out.println(t);
				//返回线程的名称
				System.out.println(t.getName());
				//返回线程的唯一id
				System.out.println(t.getId());
				// 判断线程是否可用
				System.out.println(t.isAlive());
				}

结果:

Thread[main,5,main]
main
1
true

sleep方法:线程休眠的方法
Thread.sleep()

/**
 * sleep(毫秒)
 * @author 86132
 *
 */
public class Demo3 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new Thread(()->{
			//获取正在运行的线程的名字
			System.out.println(Thread.currentThread().getName());
			for(int i=10;i>=0;i--) {
				System.out.println(i);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		,"线程sleep").start();
	}
}

练习

public static void main(String[] args) {
		// TODO Auto-generated method stub
		new Thread(()->{
			System.out.println("过年了,倒计时开始");
			for(int i=10;i>=0;i--) {
				System.out.println(i);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
				).start();

	}

join方法:等待线程执行完毕

        Thread t=new Thread() {
			public void run() {
				for(int i=1;i<=10;i++) {
					System.out.println("Thread-1"+":"+i);
			}
			}
		};
		Thread t2=new Thread(
			()->{
				for(int i=1;i<=10;i++) {
					System.out.println("Thread-2"+":"+i);
					if(i==3) {
						try {
							t.join();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}	
			});
		t.start();
		t2.start();	

结果为:

Thread-2:1
Thread-2:2
Thread-2:3
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
Thread-1:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-1:10
Thread-2:4
Thread-2:5
Thread-2:6
Thread-2:7
Thread-2:8
Thread-2:9
Thread-2:10

注意:线程执行的先后顺序是不一定的

练习:模拟图片显示,图片下载

      Thread t3=new Thread() {
			public void run() {
				for(int i=0;i<=100;i++) {
					System.out.println("图片下载进度:"+"%"+i);
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("图片下载完成");
			}
		};
		Thread t4=new Thread(()->{
			System.out.println("图片显示");
			System.out.println("图片在加载");
					try {
						t3.join();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}	
				System.out.println("图片显示成功");
			});
		t3.start();
		t4.start();

守护线程
setDeamon(true);

创建出来的线程:用户线程(前台线程)和守护线程(后台线程)
创建的线程都是用户线程
设置守护线程,调用setDeamon(true);
用法和创建方式没有区别
区别:用户线程执行完毕,守护线程无条件停止执行

rose ->用户线程,3次 
jeck ->守护线程,while(true)
public static void main(String[] args) {
		Thread rose=new Thread() {
			public void run() {
				for(int i=1;i<=3;i++) {
					System.out.println("一起跳");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("扑通。。。。。。");
			}
		};
		Thread jeck=new Thread(()->{
			while(true) {
				System.out.println("你先跳,我再跳");
				try {
					Thread.sleep(1000);
			    } catch (InterruptedException e) {
				    e.printStackTrace();
			    }
			}
		});
		rose.start();
		jeck.setDaemon(true);
		jeck.start();
	}

线程的生命周期(从生到死)
新建状态:new Thread()
就绪状态:start()
阻塞状态:sleep(),wait(),io,解除,重新排队
运行状态:run(),自动执行的
消亡:执行完run方法的语句

线程的优先级
不同优先级:高优先级先执行,低优先级后执行
同一优先级:先到先服务

线程的优先级的规范
系统自动分配:5
人为设置线程的优先级:1,5,10

注意:高优先级有优先执行的权利,但是不一定先执行
内存不够用时,先杀死的是低优先级

        Thread t1=new Thread(()->{
			for(int i=1;i<=10;i++) {
				System.out.println("t1");
			}
		});
		Thread t2=new Thread(()->{
			for(int i=1;i<=10;i++) {
				System.out.println("t2");
			}
		});
		Thread t3=new Thread(()->{
			for(int i=1;i<=10;i++) {
				System.out.println("t3");
			}
		});
		t3.setPriority(Thread.MAX_PRIORITY);
		t2.setPriority(Thread.NORM_PRIORITY);
		t1.setPriority(Thread.MIN_PRIORITY);
		
		t1.start();
		t2.start();
		t3.start();

线程同步

多个线程操作一个共享变量时,可能会导致共享变量的不完整(不安全)
为了保证共享变量的安全,使用同步机制保证共享变量的安全性

同步机制:当一个线程改变共享变量值的时候,其他线程不能使用共享变量,当线程计算完成,返回变量值之后,其他线程才可以使用共享变量

使用synchronized关键字实现线程同步
同步块synchronized

 synchronized(锁对象){
      共享变量相关的业务代码(运算)
 }

注意:锁对象只能是一个

class TicketThread extends Thread{
		public void run() {
			while(true) {
				synchronized(Demo8.class) {
				if(Demo8.ticket<=0) {
					try {
						throw new Exception("票卖完了");
					} catch (Exception e) {
						System.out.println(e.getMessage());
						break;
					}
				}else {
					Demo8.ticket--;
					System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+Demo8.ticket+"张");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				}
			}
		}
}
public class Demo8 {
	
	static int ticket=10;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TicketThread t1=new TicketThread();
		TicketThread t2=new TicketThread();
		TicketThread t3=new TicketThread();
		t1.start();
		t2.start();
		t3.start();	
	}
}

练习
线程同步,商场买衣服
商场挑衣服,异步
试衣间只有一个,试衣服是同步

class Shopping{
	public void test() {
		System.out.println(Thread.currentThread().getName()+"挑衣服");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		synchronized(this) {
		System.out.println(Thread.currentThread().getName()+"试衣服");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
	}
}
public class Demo11 {
	public static void main(String[] args) {
		Shopping s=new Shopping();
		Thread t1=new Thread(()-> {
			s.test();
		});
		Thread t2=new Thread(()-> {
			s.test();
		});
		t1.start();
		t2.start();
	}
}

在方法上synchronized,用此关键字修饰的方法叫同步方法

银行存钱: 一个账号,两个人存钱

class Bank{
	private int money=100;
	public synchronized void save(int money) {
		for(int i=0;i<3;i++) {
			this.money+=money;
			System.out.println(Thread.currentThread().getName()+"余额为"+this.money);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}
	public class Demo9 {
		public static void main(String[] args) {
			// TODO Auto-generated method stub
			Bank bank=new Bank();
			Thread t1=new Thread(()->{
				bank.save(1000);
			});
			Thread t2=new Thread(()->{
				bank.save(1000);
			});
			t1.start();
			t2.start();
		}
	}

结果:

Thread-0余额为1100
Thread-0余额为2100
Thread-0余额为3100
Thread-1余额为4100
Thread-1余额为5100
Thread-1余额为6100

同步线程之间的通信:
解决临界资源问题

wait():
notify():
notifyAll():

使用两个线程交替打印10

class NumThread implements Runnable{
	int i=1;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized(this) {
			while(i<=10) {	
				notify();   //唤醒wait的线程
				System.out.println(Thread.currentThread().getName()+":"+i);
				i++;
				try {
					if(i>10)
						break;
					wait();    //使当前线程等待
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
public class Demo10 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		NumThread num=new NumThread();
		new Thread(num).start();
		new Thread(num).start();
	}

}

结果为:

Thread-0:1
Thread-1:2
Thread-0:3
Thread-1:4
Thread-0:5
Thread-1:6
Thread-0:7
Thread-1:8
Thread-0:9
Thread-1:10

wait和sleep的区别

wait在同步块/同步方法中释放锁
银行取钱问题:sleep()方法执行之后,没有交给别的线程

sleep线程类的方法       wait是Object类的方法
sleep方法不是释放锁     wait是释放锁
sleep方法自动醒         wait必须使用notify,notifyAll唤醒

练习:生产者和消费者

class Clerk{
	private int product = 0;
	public synchronized void addProduct() {
		// TODO Auto-generated method stub
			if(product>=20) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else {
				product++;
				System.out.println("生产者生产了第"+product+"个产品");
				notifyAll();
			}
	}
	public synchronized void getProduct() {
		// TODO Auto-generated method stub
		if(product<=0){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else{
			System.out.println("消费者取走了第"+product+"个产品");
			product--;
			notifyAll();
		}
	}
}
class Productor implements Runnable{
	private Clerk c;
	public Productor(Clerk c) {
		// TODO Auto-generated constructor stub
		this.c=c;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("生产者生产产品");
		while(true){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			c.addProduct();
		}		
	}
	
}
class Consumer implements Runnable{

	private Clerk c;
	public Consumer(Clerk c) {
		// TODO Auto-generated constructor stub
		this.c=c;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("消费者消费产品");
		while(true) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			c.getProduct();
		}
	}
}
public class DemoProduct {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Clerk c=new Clerk();
		Thread t1=new Thread(new Productor(c));
		Thread t2=new Thread(new Consumer(c));
		t1.start();
		t2.start();
	}
}

练习1://12A 34B 56C 78C … 4950Y 5152Z

class NumThread implements Runnable{
	int i=1;
	char c='A';
	public void run() {
	  synchronized(this) {
		while(i<=52) {
			notify();
			String a=i+""+(i+1);
			i+=2;
			if(c<='Z') {
			notify();
			System.out.println(a+""+c);
			c+=1;
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		}
	  }
	}	
}
public class DemoTest1 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		NumThread num=new NumThread();
		new Thread(num).start();
		new Thread(num).start();
	}
}

练习二:

/* num=1 ++
0:1,2,3,4,5, 1 16 31 if(num/5%3 == 0)
1:6,7,8,9,10, 6 21 36 %3 == 1
2:11,12,13,14,15, 11 26 41 %3 == 2
0:16,17,18,19,20,
1:21,22,23,24,25,
2:26,27,28,29,30,
0:31,32,33,34,35,
1:36,37,38,39,40,
2:41,42,43,44,45,

75
*/

class OneThread implements Runnable{
	static int a=1;
	public void run() {
		  synchronized(this) {  
			  for(int num=a;num<=46;num++) {
				    notify();
					if((num/5)%3 ==0) {
						if(num==1) {
							System.out.print(0+":"+num+",");
						}else {
						System.out.print(num+",");}
					}
					if((num/5)%3 ==1) {
						System.out.print(num+",");
					}
					if((num/5)%3 ==2) {
						System.out.print(num+",");
					}
					if(num%5==0) {
						System.out.print("\n"+(num/5)%3+":");
					}
					if(num==46) {
						a=(num+1);
						try {
							wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			  
		  }
	  }
}
public class DemoTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		OneThread num=new OneThread();
		new Thread(num).start();
		new Thread(num).start();
		new Thread(num).start();


	}

}

线程的死锁

        StringBuffer str1 = new StringBuffer();
		StringBuffer str2 = new StringBuffer();
		synchronized (str1) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (str2){
				System.out.println("str1");
				System.out.println("str2");
			}
		}
		synchronized (str2) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (str1){
				System.out.println("str1");
				System.out.println("str2");
			}
		}

线程池
为什么使用线程池?
频繁的创建线程,需要耗费时间和内存

使用线程池的好处:

  1. 管理线程

  2. 使线程重用

package cn.tedu.demo.thread;

import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo {
	//创建一个线程池,如果线程池中的线程数量过大,它可以有效的回收多余的线程,
	//            如果线程数不足,那么它可以创建新的线程。
	public static void test1() throws Exception {
		 
	    ExecutorService threadPool = Executors.newCachedThreadPool();
	 
	    for (int i = 0; i < 5; i++) {
	 
	        final int index = i;
	 
	        Thread.sleep(1000);
	        //完成5个任务
	        threadPool.execute(new Runnable() {
	            @Override
	            public void run() {
	            	System.out.println(Thread.currentThread().getName() + "  " + index);
	            	 try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	            }
	        });
	    }
	}
	//指定线程池中的线程数,线程数是可以进行控制的
	public static void test2() throws InterruptedException {
		 
	   // ExecutorService threadPool = Executors.newFixedThreadPool(1);
		ExecutorService threadPool = 
				Executors.newFixedThreadPool(3);
	    for (int i = 0; i < 10; i++) {
	 
	        Thread.sleep(1000);
	        final int index = i;
	 
	        threadPool.execute(() -> {
	            try {
	                Thread.sleep(2 * 1000);
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	            System.out.println(Thread.currentThread().getName() + "  " + index);
	        });
	    }
	    threadPool.shutdown();//不关闭正在执行的线程
	  	//threadPool.shutdownNow();//不管线程是否执行完毕,立即停止执行
	  	System.out.println("关闭线程!");
	}
	//线程池支持定时周期性任务执行
	public static void test3() {
	    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
	 
	    threadPool.scheduleAtFixedRate(new Runnable() {
	        @Override
	        public void run() {
	            long start = new Date().getTime();
	            System.out.println("scheduleAtFixedRate 开始执行时间:" +
	                    DateFormat.getTimeInstance().format(new Date()));
	            try {
	                Thread.sleep(5000);
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	            long end = new Date().getTime();
	            System.out.println("scheduleAtFixedRate 执行花费时间=" + (end - start) / 1000 + "m");
	            System.out.println("scheduleAtFixedRate 执行完成时间:" + DateFormat.getTimeInstance().format(new Date()));
	            System.out.println("======================================");
	        }
	    }, 1, 5, TimeUnit.SECONDS);
	}
	//单线程池,至始至终都由一个线程来执行
	public static void test4() {
	 
	    ExecutorService threadPool = Executors.newSingleThreadExecutor();
	 
	    for (int i = 0; i < 5; i++) {
	        final int index = i;
	        threadPool.execute(() -> {
	            try {
	                Thread.sleep(2 * 1000);
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	            System.out.println(Thread.currentThread().getName() + "   " + index);
	        });
	    }
	   // threadPool.shutdown();
	}
	
	public static void main(String[] args) throws Exception {
		test2();

	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值