多线程基础

线程是CPU独立调度的基本单位,进程是资源分配的基本单位

两种创建线程的方法:

  1. 继承Thread
    • 更加适用于: 没有共享资源,各自线程使用自己的属性的情况
public class Test3 {
	public static void main(String[] args) {
		// 创建四个线程 ,并且启动线程
		Window window = new Window("窗口一");
		Window window2 = new Window("窗口二");
		Window window3 = new Window("窗口三");
		Window window4 = new Window("窗口四");
		window.start();
		window2.start();
		window3.start();
		window4.start();
	}
}

class Window extends Thread{
   int tickt = 20;		
	public Window(String name) {
		super(name);// 给线程起名字
	}
	@Override
	public void run() {
		for (int i = 1; i <= 20; i++) {
			tickt--;
			System.out.println(Thread.currentThread().getName()+"买了"+i+"张票"+"还剩"+(tickt)+"张");
		}
	}
	
}
  1. 实现Runnable接口
    • 更加适用于,拥有共享资源的情况
public class Test4 {
	public static void main(String[] args) {	
		// 叫做Runnable是夏磊对象
		Ticket ticket = new Ticket();	
		// 创建线程对象
		Thread t = new Thread(ticket, "线程一");
		Thread t2 = new Thread(ticket,"线程二");
		Thread t3 = new Thread(ticket,"线程三");
		Thread t4 = new Thread(ticket,"线程四");	
		// 四个线程共享了一个ticke他空间		
		t.start();
		t2.start();
		t3.start();
		t4.start();
		// 常用格式
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 0; i < 20; i++) {
					System.out.println("i"+i);
				}
			}
		}).start();
	}
}

class Ticket implements Runnable{	
	int tickt= 100;
	@Override
	public void run() {
		while(tickt>0){
			tickt--;
			System.out.println(Thread.currentThread().getName()+"卖了"+(100-tickt)+"张票"+"还有"+tickt+"张");
		}
	}
}

  1. 实现Callable接口
    1. 更加适用于有返回值的情况
public class Test5 {
	public static void main(String[] args) throws Exception {
		// 1.先创建实现类的对象
		MyCallable callable = new MyCallable();
		
		//2. 将callable对象装载到任务重
		FutureTask<Integer> task = new FutureTask<>(callable);
		//3. 创建线程对象
		Thread thread = new Thread(task);
		//4. 启动线程
		thread.start();
		// 线程启动以后, 就会计算结果, 计算 就可以获取返回结果
		//Integer integer = callable.call();// 仅仅是以方法调用的方式来实现的
		//System.out.println(integer);
		// 子线程执行完毕以后给返回的结果
		Integer integer = task.get();
		System.out.println(integer);
		
	}
}

class MyCallable implements Callable<Integer>{
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = 1; i <= 10000; i++) {
			sum+=i;
		}
		Thread.sleep(2000);
		return sum;
	}
}

小总结

三种创建线程的方式总结:

  • Thread
  1. 自定义类继承Trhead
  2. 重写run方法
  3. 创建子线程对象
  4. 用start方法启动
    使用: 没有共享资源,各自线程使用自己的属性的情况 四个窗口各紫卖自己的20张票
  • Runnable
  1. 实现类实现Runnable
  2. 重写run方法
  3. 创建实现类对象, 任务
  4. 创建Thread对象, 并且将实现类对象作为参数传入Thread构造方法中
  5. start 启动线程

适用于:

  1. 拥有共享资源.
  2. 如果操作比较简单 只想创建一个子线程对象的时候, 可以考虑使用线程匿名内部类的形式
new Thread(new Runnable() {			
			@Override
			public void run() {
				for (int i = 0; i < 20; i++) {
					System.out.println("i"+i);
				}
			}
	}).start();
  • 实现Callable
    1. 定义实现类 实现Callable<线程返回值类型>
    2. 重写 call()
    3. 创建实现类对象, 但是他不是一个任务
    4. 创建FutrueTask<线程返回值类型> 将线程实现类作为参数传入到FutrueTask的构造方法中
    5. 创建线程Thread对象, 并且将FutrueTask对象作为参数传入构造方法
    6. 启动线程start()

适用于: 想要得到线程结果的情况

Thread的启动方式

通常线程的启动都是通过new Thread(...).start()方法来实现的
但我们在创建线程的时候都会重写run()方法,这两个方法的区别是啥呢?

区别如下:

 start()  可以申请一个新的栈,可以启动子线程
 run()    只是一个简单的方法调用

对于多个已经开启的线程而言,每一个线程都会有自己独立的栈:

new Thread(new Myrunable()).start
new Thread(new Myrunable()).start

对于上面这段代码,在JVM中的内存结构图如下所示:
多线程基础_第1张图片

Thread类的常用方法

多线程基础_第2张图片
从上面这张图上面可以看到,Thread类的方法是非常多的,下面把一些常用的方法拿出来做一些分析:

  1. join()方法,使用这个方法,可以完成现成的插队操作,可以实现不同线程间的有序执行
public static void main(String[] args) throws InterruptedException {
        System.out.println("start...");
        //使用join来实现,先执行Thread2,再执行Thread1,再执行main线程
        Thread1 thread1 = new Thread1();
        thread1.start();
        thread1.join();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }

    static class Thread1 extends Thread{
        @Override
        public void run() {
            Thread2 thread2 = new Thread2();
            try {
                thread2.start();
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + " Thread1 : "+i);
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + " Thread2 : "+i);
            }
        }
    }

其他的方法也可以从其他文章了解:

Thread类的常用方法

线程的生命周期

多线程基础_第3张图片

实现卖票的同步

public static void main(String[] args) {
        MyRunnable my = new MyRunnable();
        for (int i = 0; i < 4; i++) {
            new Thread(my,"我是 : "+i+" 号").start();
        }
    }

    static class MyRunnable implements Runnable{
        Integer tickets = new Integer(100);
        @Override
        public void run() {
            while (true){
                synchronized (this){
                    if (tickets > 0){
                        tickets--;
                        System.out.println(Thread.currentThread().getName()+" 抢到了第 "+tickets.intValue()+"张票");
                    }else {
                        break;
                    }
                }
            }
        }
    }

lock的基本用法

static int tickets = 100;
    static Lock lock = new ReentrantLock();

    static Runnable run = () -> {
        while (true){
            lock.lock();
            try {
                if (tickets > 0){
                    tickets--;
                    System.out.println(Thread.currentThread().getName()+ " 得到 "+tickets);
                }else {
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    };

    public static void main(String[] args) {
        new Thread(run).start();
        new Thread(run).start();
    }

专门使用

你可能感兴趣的:(笔记)