线程(一)

目录

1.生命周期(五种状态--创建、就绪、运行、阻塞、死亡)

1.1 创建线程

        1.1.1 继承Thread--实例

        1.1.2 实现Runnable接口--实例

        1.1.3 实现Callable接口--实例

1.2 阻塞线程

        1.2.1 join()

        1.2.2 yield()

2.三大特性

2.1 原子性

 2.2 有序性

2.3 可见性

3.CAS

4.生产者消费者模型

4.1 概念

4.2 代码实现

        4.2.1 创建实体类--Food

        4.2.2 生产者进程 及 消费者进程

        4.2.3 定义队列

        4.2.4 调用--Main

5.线程池

5.1 概念及其优势

5.2 创建线程池

        5.2.1 创建50个线程   Executors.newCachedThreadPool();

         5.2.2 单线程  Executors.newSingleThreadExecutor();

        5.2.3 定义几个即运行几个 Executors.newFixedThreadPool(n);

5.3 创建线程池并定时执行任务

6.死锁问题

实例:循环等待

7.synchronized和ReetreeLock区别

 实例:票务系统


1.生命周期(五种状态--创建、就绪、运行、阻塞、死亡)

线程(一)

1.1 创建线程

        1.1.1 继承Thread--实例

public class Thread01 extends Thread {
	private String name;//给线程名称
	
	public Thread01(String name) {
		this.name = name;
	}
	
	//线程运行
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.name+"--"+i);//拥有时间片资源--即运行
			try {
				//线程阻塞    
                //Thread01继承Thread,则拥有Thread的方法 可以直接调用方法
				sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		//实例化对象   创建
		Thread t1 = new Thread01("A");
		Thread t2 = new Thread01("B");
		Thread t3 = new Thread01("C");
		//就绪
		t1.start();
		t2.start();
		t3.start();
		//运行结束--线程死亡
	}
}

运行结果部分截图如下所示:

线程(一)

        1.1.2 实现Runnable接口--实例

public class Thread02 implements Runnable{
	private String name;//给线程名称
	
	public Thread02(String name) {
		this.name = name;
	}
	
	//线程运行
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.name+"--"+i);//拥有时间片资源--即运行
			try {
				//线程阻塞
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
//		//实例化对象   创建
//		Thread t1 = new Thread(new Thread02("A"));
//		Thread t2 = new Thread(new Thread02("B"));
//		Thread t3 = new Thread(new Thread02("C"));
//		//就绪
//		t1.run();
//		t2.run();
//		t3.run();
//		//运行结束--线程死亡
		
		/*lambda表达式*/
//		new Thread(()-> {
//			for(int i=1;i<=10;i++) {
//				System.out.println(Thread.currentThread().getName()+"-"+i);
//				try {
//					Thread.sleep(200);
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//			}
//		}).start();;
		
		/** 匿名内部类 */
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 10; i++) {
					System.out.println(Thread.currentThread().getName()+"-"+i);
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}).start();;
	}
}

线程(一)

        1.1.3 实现Callable接口--实例

/**
 * Callable接口  : 有返回值    可抛出异常
 * @author 皮卡丘
 *
 */
public class Thread03 implements Callable {
	@Override
	public Object call() throws Exception {
		int s = 0;
		for (int i = 0; i < 1000; i++) {
			s += i;
		}
		return s;
	}

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		FutureTask<Integer> ft = new FutureTask<Integer>(new Thread03());
		ft.run();
		System.out.println("result: "+ft.get());
	}
}

线程(一)

1.2 阻塞线程

        1.2.1 join()

public class ThreadBlock extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(this.getName()+"--"+i);
		}
		try {
			sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new ThreadBlock();
		Thread t2 = new ThreadBlock();
		Thread t3 = new ThreadBlock();
		//线程交叉执行
		t1.start();
		t1.join();//阻塞其他线程,先执行t1
		t2.start();
		t3.start();
	}
}

线程(一)

        1.2.2 yield()

public class ThreadBlock extends Thread {
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			//当前值为偶数,则让出时间片,让其他线程先运行
			if(i%2==0) {
				yield();
			}
			System.out.println(this.getName()+"--"+i);
			try {
				sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		Thread t1 = new ThreadBlock();
		t1.setName("V");
		Thread t2 = new ThreadBlock();
		t2.setName("A");
		Thread t3 = new ThreadBlock();
		t3.setName("L");
		
		t1.start();
		t2.start();
		t3.start();
		
	}
}

线程(一)

2.三大特性

2.1 原子性

        线程运行步骤不可分割  (例:i++ i-- ++y --y 均不是原子操作) 

        incrementAndGet 自增并获取值

        但是原子性操作基于CAS ,会出现ABA问题(先将A修改为B,在修改回A,此时CAS是无法察觉的)解决方法:版本号+1,在修改前在判断该值,未被修改则修改,否则重新取值进行修改。

public class ThreadAtomic extends Thread {
//	public static int x = 0;
	public static final AtomicInteger x = new AtomicInteger(0);
	
	public static class AtomicThread extends Thread{
		@Override
		public void run() {
			while(true) {
//				x++;
//				System.out.println(this.getName()+"--"+x);
				System.out.println(this.getName()+"--"+x.incrementAndGet());
				try {
					sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Thread t1 = new AtomicThread();
		t1.setName("A");
		Thread t2 = new AtomicThread();
		t2.setName("B");
		Thread t3 = new AtomicThread();
		t3.setName("C");
		
		t1.start();
		t2.start();
		t3.start();
		
	}
}

线程(一)

        非原子性的执行结果会出现重复,将运行步骤拆分,存在有的线程从第一步进行操作,而有的线程从第二步操作。(例如:x++;  拆分为y = x;   y = y +1;两步)

线程(一)

 2.2 有序性

        程序执行的顺序按照代码的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

public class OrderThreadDemo {

	private  static volatile OrderThreadDemo instance = null;
	
	private OrderThreadDemo() {}
	
	
	public static OrderThreadDemo getInstance() {
		/**
		 * 两个线程进入方法
		 *   if   instance == null 
		 *   准备创建对象  instance = new OrderThreadDemo();
		 *   可能分为三个流程:
		 *     1. 开辟内存空间
		 *     2. 初始化对象
		 *     3. 引用和对象绑定
		 *     
		 *     另外一种方式:
		 *      1. 开辟内存空间
		 *      2. 引用和对象绑定   instance--->对象,instance不是null
		 *      3. 初始化对象(未完成)
		 */
		
		 if(instance==null) {//判断OrderThreadDemo对象是否存在
			 instance = new OrderThreadDemo();
		 }
		 
		 return instance;
	}

}

2.3 可见性

         使用volatile关键字(将修改的值立刻更新到主存中,对其他线程有作用  (共享)),即一个线程修改后,通知到所有线程
         线程运行将资源复制到工作内存中,在其中修改对其他线程不起作用

public class ThreadVisable extends Thread {
//	public static boolean f = false;
	public static volatile boolean f = false;
	
	public static class A extends Thread{
		@Override
		public void run() {
			while(true) {
				//当f为true时,打印语句
				if(f) {
					System.out.println(this.getName()+"--"+f);
					break;
				}
			}
		}
	}
	
	public static class B extends Thread{
		@Override
		public void run() {
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			f = true;
			System.out.println(this.getName()+"::"+f);
		}
	}
	
	public static void main(String[] args) {
		Thread t1 = new A();
		t1.setName("A");
		Thread t2 = new B();
		t2.setName("B");
		
		t1.start();
		t2.start();
	}
}

线程(一)

未使用关键字,则一个线程修改后对其他线程无影响-->体现不出可见性

线程(一)

3.CAS

  • 它将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值。
  • 操作结果必须说明是否进行替换; 这可以通过一个简单的布尔响应(这个变体通常称为比较和设置),或通过返回从内存位置读取的值来完成。
  • 比较和交换 作为单个原子操作完成, 原子性保证新值基于最新信息计算; 如果该值在同一时间被另一个线程更新,则写入将失败。

线程(一)

         当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会挂起,仅是被告知失败,并且允许再次尝试,当然也允许实现的线程放弃操作。 基于这样的原理,CAS 操作即使没有锁,也可以发现其他线程对当前线程的干扰。

4.生产者消费者模型

4.1 概念

线程(一)

         生产者和消费者的任务很明确,生产者只管生产数据,然后添加到缓冲队列。而消费者只管从缓冲队列中获取数据。

4.2 代码实现

        4.2.1 创建实体类--Food

public class Food{
	private String name;
	
	public Food(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Food [name=" + name + "]";
	}	
}

        4.2.2 生产者进程 及 消费者进程

public class ProducerThread extends Thread{
	private KFC kfc;

	public ProducerThread(KFC kfc) {
		this.kfc = kfc;
	}
	
	@Override
		public void run() {
			while(true) {
				kfc.produce();
				try {
					sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}
}
public class ConsumerThread extends Thread {
	private KFC kfc;

	public ConsumerThread(KFC kfc) {
		this.kfc = kfc;
	}
	
	@Override
		public void run() {
			while(true) {
				kfc.consume();
				try {
					sleep(400);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
}

        4.2.3 定义队列

public class KFC {
	//定义缓存事物队列
	private Deque<Food> q = new LinkedList<Food>();
	private final int MAX_SIZE = 10;
	
	/**
	 * 生产食物
	 */
	public synchronized void produce() {
		while(q.size()==MAX_SIZE) {
			try {
				System.out.println("队列已满,生产者休息等待消费者消费");
				wait();//阻塞进程     wait()会释放锁,要将方法变为同步方法(加suo)
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Food food = new Food("鸡肉饼");
		q.offer(food);
		System.out.println("生产者生产一个"+food+",还有食物"+q.size());
		notify();//欢迎其他的进程
	}
	
	/**
	 * 消费食物
	 */
	public synchronized void consume() {
		while(q.isEmpty()) {//队列为空 消费者不能消费
			System.out.println("队列为空,消费者休息,等待生产者生产......");
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Food food = q.poll();
		System.out.println("消费者,消费一个:"+food+",还有食物"+q.size());
		notifyAll();
	}	
}

        4.2.4 调用--Main

public class Main {
	public static void main(String[] args) {
		KFC kfc = new KFC();
		
		Thread p1 = new ProducerThread(kfc);
		Thread p2 = new ProducerThread(kfc);
		Thread p3 = new ProducerThread(kfc);
		
		Thread c1 = new ConsumerThread(kfc);
		Thread c2 = new ConsumerThread(kfc);
		Thread c3 = new ConsumerThread(kfc);
		Thread c4 = new ConsumerThread(kfc);
		Thread c5 = new ConsumerThread(kfc);
		
		p1.start();
		p2.start();
		p3.start();
		
		c1.start();
		c2.start();
		c3.start();
		c4.start();
		c5.start();
	}
}

运行结果:

线程(一)

5.线程池

5.1 概念及其优势

        线程池:一个线程执行结束后不马上销毁,继续执行新的任务,这样就节省了不断创建线程和销毁线程的开销。

        优势:

  • 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
  • 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执
  • 行;
  • 方便线程并发数的管控。
  • 提供更强大的功能,延时定时线程池

5.2 创建线程池

        5.2.1 创建50个线程   Executors.newCachedThreadPool();

public class poolDemo {
	public static void main(String[] args) {
		int count = 0;
		//创建50个线程   10*5
		Executor pool = Executors.newCachedThreadPool();
		while(count++ < 10) {
			pool.execute(()->{
				for(int i=0;i<5;i++) {
					System.out.println(Thread.currentThread().getName()+"::"+i);
				}
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			});
		}
	}
}

线程(一)

         5.2.2 单线程  Executors.newSingleThreadExecutor();

public class poolDemo {
	public static void main(String[] args) {
		int count = 0;
		//单线程运行--一个线程循环执行
		Executor pool = Executors.newSingleThreadExecutor();
		while(count++ < 10) {
			pool.execute(()->{
				for(int i=0;i<5;i++) {
					System.out.println(Thread.currentThread().getName()+"::"+i);
				}
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			});
		}
	}
}

线程(一)

        5.2.3 定义几个即运行几个 Executors.newFixedThreadPool(n);

public class poolDemo {
	public static void main(String[] args) {
		int count = 0;
		//定义几个线程就是几个线程在跑
		Executor pool = Executors.newFixedThreadPool(2);
		while(count++ < 10) {
			pool.execute(()->{
				for(int i=0;i<5;i++) {
					System.out.println(Thread.currentThread().getName()+"::"+i);
				}
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			});
		}
	}
}

线程(一)

5.3 创建线程池并定时执行任务

public class poolDemo {
	public static void main(String[] args) {
		ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
		//es.scheduleAtFixedRate(command, 延迟时间, 间隔时间, 事件类型);
		es.scheduleAtFixedRate(()->{System.out.println("当前时间:"+new Date());}, 0, 1, TimeUnit.SECONDS);
	}
}

线程(一)

6.死锁问题

线程(一)

实例:循环等待

public class Resource {
	private String name;
	
	public Resource(String name) {
		this.name=name;
	}

	@Override
	public String toString() {
		return "Resource [name=" + name + "]";
	}	
}
public class DeadLock implements Runnable {
	Resource a = new Resource("A");
	Resource b = new Resource("B");

	public DeadLock(Resource a,Resource b) {
		this.a = a;
		this.b = b;
	}
	
	@Override
	public void run() {
		System.out.println("准备锁资源:"+a+"......");
		synchronized (a) {
			System.out.println("资源:"+a+"锁定成功");
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("准备锁资源:"+b+"......");
			synchronized (b) {
				System.out.println("资源:"+b+"锁定成功");
			}
			System.out.println("资源"+b+"释放成功");
		}
		System.out.println("资源"+a+"释放成功");
	}
}
public class DeadLockTest {
	public static void main(String[] args) {
		Resource a = new Resource("A");
		Resource b = new Resource("B");
		Resource c = new Resource("C");
		
		//线程实现ruannable 定义对象的方法
		Thread t1 = new Thread(new DeadLock(a, b));
		Thread t2 = new Thread(new DeadLock(b, c));
		Thread t3 = new Thread(new DeadLock(c, a));
		
		t1.start();
		t2.start();
		t3.start();
	}
}

 执行结果如下所示:线程(一)

7.synchronized和ReetreeLock区别

synchronized ReetrantLock
不需要手动释放锁和开启锁 使用灵活,但必须有释放锁动作
对象之间是互斥关系 手动释放和开启锁
只适用于代码块锁

 实例:票务系统

public class Ticket {
	private int num = 100;
	private static final ReentrantLock lock = new ReentrantLock();//可重入锁   使用灵活  手动关锁,避免死锁
	
	//卖票
	public void sold(String name) {
		lock.lock();
		try {
			int s = --num;
			System.out.println(name+",卖出一张票,剩余票数:"+s);
		} finally{
			lock.unlock();
		}
	}
	//获取剩余票数
	public int getNum() {
		return num;
	}
}
public class TicketThread extends Thread{
	private Ticket ticket;
	private String name;
	
	public TicketThread(Ticket ticket,String name) {
		this.ticket = ticket;
		this.name = name;
	}
	
	@Override
	public void run() {
		while(ticket.getNum()>0) {
			ticket.sold(name);
			try {
				sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
public class TicketTest {
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		TicketThread t1 = new TicketThread(ticket, "A");
		TicketThread t2 = new TicketThread(ticket, "B");
		TicketThread t3 = new TicketThread(ticket, "C");
		
		t1.start();
		t2.start();
		t3.start();
	}
}

 执行结果如下图所示:

线程(一)

 

上一篇:【Groovy】Groovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )


下一篇:基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(5)失败用例截图与重试