java---多线程
一、线程简介
线程:程序可执行的路径,或者说程序可执行的通道
进程:一个个单独执行的程序或软件
进程和线程的关系:线程是进程执行的路径、通道,多线程即有多个路径执行这个程序
一个进程如果只有一个线程,那么这是个单线程程序,如果有多条线程,则为多线程程序
二、生命周期
创建多线程的方法:继承Thead父类和实现Runable接口
1、继承Thead
public class TheadDemo extends Thead{
public void run()
puclic static void main(String[] args){}
}
public class TheadDemo1 extends Thread {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args){
TheadDemo1 theadDemo1 = new TheadDemo1();
TheadDemo1 theadDemo2 = new TheadDemo1();
theadDemo1.start();
theadDemo2.start();
}
2、实现Runable接口
public class TheadDemo Implements Runable{
puclic void run()
puclic static void main(String[] args){}
}
public class RunableDemo implements Runnable {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+""+i); // Thread.currentThread() 获得当前执行线程的信息 getName() 得到线程的名字
}
}
public static void main(String[] args){
RunableDemo runableDemo = new RunableDemo();
RunableDemo runableDemo1 = new RunableDemo();
Thread thread = new Thread(runableDemo,"线程一"); //new Thead( 要创建线程的类对象, 给线程起的名字);
Thread thread1 = new Thread(runableDemo1, "线程二");
thread.start();
thread1.start();
}
}
run()方法:当一个线程启动后,就会自动去执行这个方法,当有多个线程的时候每个线程都会去调用run()方法,多个线程是没有执行顺序的,是由cpu随机分配,多个线程依次执行。main()方法主线程是不会去调用外部的run()方法的
两种实现接口方法的区别:
Thead:
优点:使用方便 缺点:单继承的局限性
Runable:
优点:可以多继承 缺点:使用麻烦
三、常用的方法:
setPriority(int new Priority):更改线程的优先级,默认值是5,范围是1-10。1的优先级最低,10的优先级最高
join():插入:在当前的线程执行中,如果使用join()插入一个线程,那么就会先执行插入的线程,并且要执行完毕才会再执行原来的线程
sleep(long millis):设置线程休眠,时间以毫秒为单位。设置线程在指定的时间内休眠,不运行。
yield()暂停正在运行的线程,礼让执行:该方法就像公交车上的让座,被让座的可能接收也可能不接受,所以线程既可能执行原来的也可能执行插入进来的线程
案例:
slepp和setPriority:
join():
public class JoinDemo implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) throws InterruptedException {
JoinDemo joinDemo = new JoinDemo();
Thread thread = new Thread(joinDemo);
thread.start();
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName());
if(i==3){
//join() 插队执行
thread.join();
//yield() 礼让执行,可能礼让成功,也可能失败
//Thread.yield();
}
}
}
}
四、同步锁
当多个线程操作同一共享资源时,将引发数据不安全问题,如下:利用线程休眠来模拟网络延迟,多个线程去一家商店买裤子
public class BuyGoodsDemo implements Runnable {
private int num=10; //裤子总数
private int count=0;//购买到的是第几条裤子
@Override
public void run() {
//每一个人买过之后,num减一,count加一
while(true){
if(num<=0){
//裤子卖完,跳出循环
break;
}
num--;
count++;
try{
//模拟网络延迟
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到的是第"+count+"条裤子,还有"+num+"条");
}
}
public static void main(String[] args){
BuyGoodsDemo buyGoodsDemo = new BuyGoodsDemo();
Thread t1 = new Thread(buyGoodsDemo, "小敏");
Thread t2 = new Thread(buyGoodsDemo, "小青");
Thread t3 = new Thread(buyGoodsDemo, "小红");
Thread t4 = new Thread(buyGoodsDemo, "小白");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
执行后的结果:
原因:这时因为当一个线程执行run()方法后,num和count被操作,但是再往后执行就出现了网络延迟,第一个执行的线程还有执行完毕,后边的线程已经将num和count执行了,当网络恢复了,
第一个线程再向下执行,此时的每个参数数值已经是最后一个线程执行后的参数数值
解决方法:synchronized:同步锁,将容易出现问题的代码进行包裹,包裹后就成了单线程。
两种写法:同步代码块和同步方法
第一种:同步代码块:
public class BuyGoodsDemo1 implements Runnable {
/**
* synchronized:同步锁,将容易出现问题的代码进行包裹,包裹后就成为了单线程
* 一、同步代码块(该例中用同步代码块)
* 二、同步方法
* */
private int num=10;
private int count=0;//购买到的是第几条裤子
@Override
public void run() {
//每一个人买过之后,num减一,count加一
while(true){
//...........................................................................................................................
synchronized (this){
if(num<=0){
//裤子卖完,跳出循环
break;
}
num--;
count++;
System.out.println(Thread.currentThread().getName()+"买到的是第"+count+"条裤子,还有"+num+"条");
}
//...............................................................................................................................
try{
//模拟网络延迟
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
BuyGoodsDemo1 buyGoodsDemo = new BuyGoodsDemo1();
Thread t1 = new Thread(buyGoodsDemo, "小敏");
Thread t2 = new Thread(buyGoodsDemo, "小青");
Thread t3 = new Thread(buyGoodsDemo, "小红");
Thread t4 = new Thread(buyGoodsDemo, "小白");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
第二种:同步方法
public class BuyGoodsDemo2 implements Runnable {
/**
* synchronized:同步锁,将容易出现问题的代码进行包裹,包裹后就成为了单线程
* 一、同步代码块
* 二、同步方法(该例中用同步方法)
* */
private int num=10;
private int count=0;//购买到的是第几条裤子
@Override
public void run() {
//每一个人买过之后,num减一,count加一
while(true){
if(!buy()){
break;
}
try{
//模拟网络延迟
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public synchronized boolean buy(){
if(num<=0){
//裤子卖完,跳出循环
return false;
}
num--;
count++;
System.out.println(Thread.currentThread().getName()+"买到的是第"+count+"条裤子,还有"+num+"条");
return true;
}
public static void main(String[] args){
BuyGoodsDemo2 buyGoodsDemo = new BuyGoodsDemo2();
Thread t1 = new Thread(buyGoodsDemo, "小敏");
Thread t2 = new Thread(buyGoodsDemo, "小青");
Thread t3 = new Thread(buyGoodsDemo, "小红");
Thread t4 = new Thread(buyGoodsDemo, "小白");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
练习:
一、 使用多线程模拟年轻人与老人年徒步爬到1500米的山顶
需求讲解:
年轻人每爬100米需要300毫秒,
老人年爬完100米需要1000毫秒,
利用线程的休眠分别模拟老年人与年轻人每爬100米需要的时间,实现该案例。
二、 利用多线程模拟叫号看病
需求讲解:
1、某科室一天需看普通号50个,特需号10个
2、特需号看病时间是普通号的2倍(设置线程的休眠)
3、开始时普通号和特需号并行叫号(两个线程同时分别被调度),叫到特需号的概率比普通号高(设置线程的优先级)
4、当普通号叫完第10号时,要求先看完全部特需号,再看普通号(join方法)
使用多线程模拟这一过程,执行效果大致如下:
代码:第一题:
public class HomeWorkThead implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
try {
Thread.sleep(3000);//设置vip号的诊断时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+i);
}
}
public static void main(String[] args){
HomeWorkThead homeWorkThead = new HomeWorkThead();
Thread thread = new Thread(homeWorkThead,"vip号");
//设置主线程为普通号
Thread.currentThread().setName("普通号");
thread.setPriority(10);//设置vip号的优先级
thread.start();
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+i);
if(i==10){
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
第二题代码:
public class HomeWorkThead2 implements Runnable {
@Override
public void run() {
if(Thread.currentThread().getName().equals("年轻人")) {
for (int i = 0; i < 15; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "跑完一百米");
if(i==14){
System.out.println(Thread.currentThread().getName()+"跑完了");
}
}
}
if(Thread.currentThread().getName().equals("老年人")){for (int i = 0; i < 15; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "跑完一百米");
if(i==14){
System.out.println(Thread.currentThread().getName()+"跑完了");
}
}}
}
public static void main(String[] args){
HomeWorkThead2 h1 = new HomeWorkThead2();
HomeWorkThead2 h2 = new HomeWorkThead2();
Thread t1 = new Thread(h1, "年轻人");
Thread t2 = new Thread(h2, "老年人");
t1.start();
t2.start();
}
}