从零开始学Java-06多线程

一、进程和线程

  • 进程 :电脑中每一个单独运行的程序都是一个独立的进程,进程之间是相互独立的
  • 线程:线程依赖于进程的支持,是进程中的最小执行单位
  • 多线程:一个时间段上可以同时运行多个程序,程序将进行资源的轮流抢占

Java是多线程的编程语言,Java在进行并发访问的处理的时候,可以得到更高的处理性能
线程是一个动态执行的过程,它是一个从产生到死亡的过程,其生命周期如下

在这里插入图片描述

线程的优先级

每一个Java线程都有一个优先级,取值范围为1到10,默认为5

二、 创建线程

1、继承Thread类本身

继承Thread类,重写run方法,调用start方法开启线程

public class Test {
public static void main(String[] args) {
          MyThread mt = new MyThread();
		  mt.start();//mt.start()调用系统资源,启动新的线程,新的线程会主动执行run()方法,谁start的就
          for (int j= 0; j< args.length; j++) {
                   System.out.println("main j = "+j);
          }
}
}
//定义一个类继承Thread
class MyThread extends Thread{
          @Override
          //重写run方法
          public void run() {
                   for (int i = 0; i < 20; i++) {
                             System.out.println("run i = "+i);
                   }
          }
}

2、实现Runnable接口

实现Runnable接口,调用run方法,调用start执行

 public static void main(String[] args) {
                   // 3.创建Runnable实现类对象
                   MyRunnable mr = new MyRunnable();
                   // 4.创建Thread并传入实现类对象
                   Thread t = new Thread(mr);
                   // 5.启动线程
                   t.start();
                   for (int i = 0; i < 20; i++) {
                             System.out.println("main: " + i);
                   }
          }
}
// 1.定义Runnable接口的实现类型
class MyRunnable implements Runnable {
          // 2.重写run方法
          @Override
          public void run() { // run方法在子线程上面执行
                   for (int i = 0; i < 20; i++) {
                             System.out.println("run: " + i);
                   }
          }
}

3、通过Callable和 Future 创建线程

1、创建Callable的接口实现类并实现call方法
2、创建Callable实现类的实例,使用FutureTask类来包装Callable对象
3、使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程
4、调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值

public class CallableThreadTest implements Callable<Integer> {
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的线程").start();  
            }  
        }  
        try  
        {  
            System.out.println("子线程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
}

三、多线程

原理:CPU同一时间只能处理一个线程,其实是CPU在多个线程之间快速切换造成的"假象

  • 创建线程需要消耗CPU和内存线程太多CPU切换太频繁,每个线程执行到的机会就少了
  • 好处:不会提高程序的执行速度,让电脑同时干活,可以提CPU的使用效率,提高资源的利用率

线程安全

多个线程访问某个方法的时候,不管这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。

如何确保线程安全

synchronized 同步代码块
synchronize(锁对象){
    //要同步的代码块
}

操作共享资源的代码需要被同步;
保证同步代码块里面的代码同时只有一个线程操作,其他的线程在外面等
必须使用同一个对象作为锁

public class demo2 {
	public static void main(String[] args) {
		SaleTicket t1 = new SaleTicket();
		t1.setName("窗口1");
		SaleTicket t2 = new SaleTicket();
		t2.setName("窗口2");
		SaleTicket t3 = new SaleTicket();
		t3.setName("窗口3");
		t1.start(); // t1.start() -> 新的线程执行t1.run();
		t2.start(); // t2.start() -> 新的线程执行t2.run();
		t3.start(); // t3.start() -> 新的线程执行t3.run();
	}
}
// 卖票的线程
class SaleTicket extends Thread {
	private static int ticketNum = 100; // ticketNum = 1
	private static Object obj = new Object();
	@Override
	public void run() {
		while (true) {
		// 任意对象都可以作为锁,但是必须是同一把锁
			synchronized (obj) { // t2 // t1
		 	// 操作共享数据的代码都需要锁住
		 	if (ticketNum > 0) {
	           try {
	               Thread.sleep(10);
	           } catch (InterruptedException e) {}
				ticketNum--;
    			System.out.println(getName() + " 卖了一张票,剩余 " + ticketNum);
              } else {
				break;
           		}
               }
               Thread.yield(); // t3 //
                  // 当CPU执行到这行代码,告诉CPU,我这个线程不太想执行了,CPU切换到其他线程去,尽量切换
                             // 礼让
                             // try {
                             // Thread.sleep(2);
                             // } catch (InterruptedException e) {
                             // e.printStackTrace();
                             // }
                   }
          }
}
lock 锁

提供了比synchronize方法和语句可获得的更广泛的锁定操作。

class SaleTicket extends Thread {
		private static int ticketNum = 100; // ticketNum = 1
		// 锁要是同一把
		private static Lock lock = new ReentrantLock();
		@Override
		public void run() {
			while (true) {
			// 任意对象都可以作为锁,但是必须是同一把锁
			//                           synchronized (obj) { // 在同步代码块开始的地方上锁
				lock.lock(); // 上锁   // t3
				try {
				// 操作共享数据的代码都需要锁住
					if (ticketNum > 0) { // t2
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
						}
						ticketNum--;
						System.out.println(getName() + " 卖了一张票,剩余 " + ticketNum);
					} else {
						break;
						}
						} finally {
							lock.unlock();
					}
				//                           } // 在同步代码块结束的地方释放锁
				Thread.yield(); // t1 // 当CPU执行到这行代码,告诉CPU,我这个线程不太想执行了,CPU切换到其他线程去,尽量切换
			}
		}
}

线程池

Runnable接口
  • 定义一个类实现Runnable接口
  • 重写run方法
  • 创建Runnable实现类对象
  • 创建一个线程池
  • 提交任务
  • 关闭线程池

线程池的执行任务过程

  • 刚创建好的线程池,没有任务,线程池中的线程会等待
  • 当添加一个任务的时候,线程池会派其中一个线程去执行这个任务
  • 如果线程池中的任务比线程多的时候,后面的任务会等待,当前面的线程执行完任务,就会来执行后面的任务
  • 如果任务全部执行完成,线程池中的线程也接着等待
  • 当线程池销毁的时候,里面的线程也销毁
public static void main(String[] args) {
                   // 3.创建Runnable实现类对象
                   MyRunnable mr = new MyRunnable();
                   // 4.创建一个有固定线程数量的线程池
                   ExecutorService pool = Executors.newFixedThreadPool(3);
                   // ThreadPoolExecutor
//                 System.out.println(pool); 
                   // 5.往提交任务
                   pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
                   pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
                   pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法
                   pool.submit(mr); // 线程池就派其中的一个线程来执行mr中的run方法   
                   // 6.关闭线程池
                   pool.shutdown();
          }
}
// 1.定义一个类实现Runnable接口
class MyRunnable implements Runnable {
          // 2.重写run方法
          @Override
          public void run() { // 在子线程上面执行的任务
                   String name = Thread.currentThread().getName();
                   for (int i = 0; i < 1; i++) {
                             System.out.println(name + " : " + i);
                   }
          }
Callable接口

作用是放在子线程上执行代码

  • 定义一个类实现Callable接口
  • 重写call方法
  • 创建Callable接口实现类对象
  • 创建线程池
  • 往线程池里面提交方法
  • 关闭线程池
  • *可以同时往线程池中提交Runnable和Callable任务
public static void main(String[] args) {
                   // 3.创建Callable接口实现类对象
                   MyCallable mc = new MyCallable();
                   MyRunnable mr = new MyRunnable();
                   // 4.创建线程池
                   ExecutorService pool = Executors.newFixedThreadPool(3);
                   // 5.往线程池里面提交任务
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mc); // 线程池会派一个线程来执行mc的call方法
                   pool.submit(mr);    
                   // 6.关闭线程池
                   pool.shutdown();
          }
}
// 1.定义一个类实现Callable接口
class MyCallable implements Callable {
          // 2.重写call方法
          @Override
          public Object call() throws Exception {
                   String name = Thread.currentThread().getName();
                   for (int i = 0; i < 1; i++) {
                             System.out.println(name + " : " + i);
                   }
                   return null;
          }
}
//1.定义一个类实现Runnable接口
class MyRunnable implements Runnable {
          // 2.重写run方法
          @Override
          public void run() { // 在子线程上面执行的任务
                   String name = Thread.currentThread().getName();
                   for (int i = 0; i < 1; i++) {
                             System.out.println(name + " 222222: " + i);
                   }
          }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值