多线程实现、周期、控制方法

1、进程与线程区别:
在操作系统中打开一个记事本,就是启动一个程序,代表着操作系统会分配一块内存给这个程序进程,一个进程至少有一个线程,线程是最小的执行单元,可以共享进程的数据,开销比较小;

打开一个程序就会开启一个进程,一个进至少有一个线程,多个线程之间可以共享进程的数据;

2、并发与并行的区别:
并行:是指同一时刻有多个指令在处理器上执行;是同时进行;
并发:指一个时刻只能有一个指令得到执行,但是会快速的轮换执行多个指令

我们打开自己的操作系统电脑开着音乐,开着记事本 而操作系统CPU在执行时是轮换执行各个进程的命令,只是CPU的处理速度比较快,我们感觉不到;

3、创建多线程:

方法一:继承Thread类

public class ThreadTest extends Thread {
	
   public ThreadTest(String name) {
		super(name);
	}
	
	@Override
	public void run() {
		for (int i=0;i<10;i++) {
			System.out.println(currentThread().getName()+"  is running"+i);
		}
	   	
	}
	
	public static void main(String[] args) {
		//1------自定义类继承Thread类
		ThreadTest newThreadTest=new ThreadTest("new Thread");
		//2------启动线程
		newThreadTest.start();
		for (int i=0;i<10;i++) {
			System.out.println(currentThread().getName()+"  is running"+i);
		}
	   	
	}

}
main  is running0
main  is running1
main  is running2
main  is running3
main  is running4
main  is running5
main  is running6
main  is running7
main  is running8
main  is running9
new Thread  is running0
new Thread  is running1
new Thread  is running2
new Thread  is running3
new Thread  is running4
new Thread  is running5
new Thread  is running6
new Thread  is running7
new Thread  is running8
new Thread  is running9

方法二:实现Runnable接口

public class InterfaceThead  implements Runnable{

	@Override
	public void run() {
		for (int i=0;i<10;i++) {
			System.out.println("my thread "+"  is running"+i);
		}		
	}

	public static void main(String[] args) {
		//1----定义一个类实现Runnable接口并重写其中的run方法
		InterfaceThead threadInterfaceThead=new InterfaceThead();
		//2----将runnable作为Thread 的构造参数传入target
		Thread tesThread=new Thread(threadInterfaceThead);
		//3-----启动线程
		tesThread.start();
	}
}
my thread   is running0
my thread   is running1
my thread   is running2
my thread   is running3
my thread   is running4
my thread   is running5
my thread   is running6
my thread   is running7
my thread   is running8
my thread   is running9

方法三:

public class CallableThread  implements Callable<String>{

	@Override
	public String call() throws Exception {
		System.out.println("开始调用新的线程!");
		return "有返回值的线程!";
	}

	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//1-----FutureTask包装callable对象,其中callable要重写call方法
		FutureTask task=new FutureTask<String>(new CallableThread());
		//2-----将futureTask作为new Thread(target,name)
		  Thread thread=new Thread(task, "callableThread");
		//3-----启动线程
		  thread.start();
		  //4-----接收返回的值
		  if(task.get() != null) {
			  System.out.println(task.get());
		  }
	}
}

结论:无论使用哪种方式,都调用的是Thread的run方法,如果不适用方法一,不覆盖Thread的run方法的话,使用方法二与方法三,传入一个target则会调用Runable或者Callable的run方法:请查看源码Thread的run方法源码:

   @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

4、线程周期:
在这里插入图片描述
本图片来源于网络

线程的5种状态:

新建状态:new 在使用方法new Thread()系统就会为线程分配内存,处于就绪状态;

就绪状态:runnable 使用Thread 的start的方法是线程处于就绪状态等待JVM的调用;

运行状态:run JVM开始调用该线程

阻塞状态:blocked 使其他的线程暂时获得执行的资源的机会,暂且将正在执行的线程阻塞,一般是采用的抢占式调度策略,也可以使用sleep或者yield暂且主动让出当前的资源;

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;wait会释放持有的锁

2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期

5、控制线程:
在这里插入图片描述
图片来源:https://www.jianshu.com/p/3c85daeba1a5
sleep方法

/**
 * SleepTest 继承线程Thread类,每1秒打印一次时间;
 * 主线程sleep 9秒,
 * interrupt子线程
 * 
 *
 */
public class SleepTest extends Thread {

	@Override
	public void run() {
		while(true) {
			System.out.println("now time is "+new Date());
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				System.out.println(currentThread().getName()+"is be interruped!");
				return ;
			}
			
		}
	}
	
	public static void main(String[] args) {
		SleepTest test=new SleepTest();
		test.start();
		
		try {
			Thread.sleep(9000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(currentThread().getName()+" is running!");
		test.interrupt();
	}
}
now time is Thu Sep 12 15:43:34 CST 2019
now time is Thu Sep 12 15:43:35 CST 2019
now time is Thu Sep 12 15:43:36 CST 2019
now time is Thu Sep 12 15:43:37 CST 2019
now time is Thu Sep 12 15:43:38 CST 2019
now time is Thu Sep 12 15:43:39 CST 2019
now time is Thu Sep 12 15:43:40 CST 2019
now time is Thu Sep 12 15:43:41 CST 2019
now time is Thu Sep 12 15:43:42 CST 2019
main is running!
Thread-0is be interruped!

yield方法

/**
 * yield方法调用后,线程会暂时处于就绪状态等待JVM的调用,可能就会立即被调用
 * 
 *
 */
public class YieldTest extends Thread{

	@Override
	public void run() {
		for(int i=0;i<20;i++) {
			 System.out.println(currentThread().getName()+" is running  "+i);
			if(i%2==0) {
				Thread.yield();
			}
		}
	}
	
	
	public static void main(String[] args) {
		YieldTest test1=new YieldTest();
		YieldTest test2=new YieldTest();
		test1.start();
		test2.start();
	}
}
Thread-0 is running  10
Thread-1 is running  8
Thread-0 is running  11
Thread-0 is running  12
Thread-1 is running  9
Thread-1 is running  10
Thread-0 is running  13
Thread-0 is running  14
Thread-1 is running  11
Thread-1 is running  12
Thread-0 is running  15
Thread-0 is running  16
Thread-0 is running  17
Thread-0 is running  18
Thread-1 is running  13
Thread-1 is running  14
Thread-0 is running  19
Thread-1 is running  15
Thread-1 is running  16
Thread-1 is running  17
Thread-1 is running  18
Thread-1 is running  19

join方法

/*
 * join就是等待线程执行完毕后才能执行另一个线程
 */
public class JoinTest extends Thread {

	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(currentThread().getName()+"  is  running "+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		JoinTest test=new JoinTest();
		test.start();
		try {
			test.join(9000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("i am the main thread");
	}
}
Thread-0  is  running 0
Thread-0  is  running 1
Thread-0  is  running 2
Thread-0  is  running 3
Thread-0  is  running 4
Thread-0  is  running 5
Thread-0  is  running 6
Thread-0  is  running 7
Thread-0  is  running 8
i am the main thread
Thread-0  is  running 9

wait方法

/**
 * 
 * 模拟一个存钱取钱的过程:
 * 要求必须先将钱存进去才能取出
 *
 */
public class WaitTest {
	//定义一个属性
	private int i;
	
   public static void main(String[] args) {
	 WaitTest tetsTest=new WaitTest();
	 PutInto into=new PutInto(tetsTest); 
	 PutOut out=new PutOut(tetsTest);
	 into.start();
	 out.start();
	 
  }
	
   public synchronized void putIntoMoney(){
	   //如果现在钱不是0,就要先取出来,等待其他的线程取钱,释放锁
	   if (i!=0) {
		try {
			wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	   //值为0时需要加把钱放进去了
	   i++;
	   System.out.println("我把钱放进去了!");
	   //通知其他的可以进行取钱了
       notify();
   }
	
   public synchronized void putOutMoney() {
	   //值为0需要先将钱放进去,等待其他线程放钱进去,先释放锁
         if(i==0) {
        	try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
         }
         //值为1时需要加把钱放取出了
         i--;
         System.out.println("我把钱取出来了!");
       //通知其他的可以进行放钱了
         notify();
   }
}  
   //----放钱的线程--------
   class PutInto extends Thread{
	   //模拟人
	   private WaitTest per;
	   
	 public  PutInto(WaitTest per){
		  this.per=per; 
	   }
	 
	 @Override
	public void run() {
		 for (int i = 0; i < 20; i++) {
	            per.putIntoMoney();
	        }

	 }
	}
   
   
   
   //------取钱的线程-------
   
   class PutOut extends Thread{
	   //模拟人
	   private WaitTest per;
	   
	 public  PutOut(WaitTest per){
		  this.per=per; 
	   }
	 
	 @Override
	public void run() {
		 for (int i = 0; i < 20; i++) {
	            per.putOutMoney();
	        }
	 }
	}

我把钱取出来了!
我把钱放进去了!
我把钱取出来了!
我把钱放进去了!
我把钱取出来了!
。。。。。。

总结下应用吧:

join方法:
等待一个线程完毕后才能执行下一个线程,会造成线程阻塞,比如你排队去取钱,只有上一个顾客被服务完,才能轮到你;

yield方法:
让正在执行的线程处于阻塞状态,可能线程调用此方法后立即获得执行的机会,也可能会让给优先级比它高的线程;

sleep方法:
和yield一样不会释放锁,使线程处于阻塞状态,sleep(time)超时会自动唤醒

wait方法
是在synchronized的同步代码块中使用
wait是Object的方法,在调用中会释放锁,如果多个对象对此对象进行操作就会获得此对象的锁;
可以使用notify唤醒在此同步监视器上的一个线程;
nitifyall唤醒在此同步监视器的所有线程;

6、线程池的启动策略

1、线程池刚创建时,里面没有一个线程,任务队列是作为参数传进来,不过,就算队列里面没有任务,线程池也不会马上执行他们;

2、当调用execute()方法添加一个任务时,线程池会做如下的判断:

在这里插入图片描述
图片来源与网络

下一章节我们讲解下线程的安全问题及线程池

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值