Java基础系列八之线程

JVM:Java虚拟机 识别main(主线程)
  面试题:
   JVM是多线程程序吗?至少有几条线程..
   jvm是多线程的,
   至少有2条线程...
   有主线程,main..执行这些代码,能够被Jvm识别
       垃圾回收线程
   在执行程序的时候,一些对象会被Jvm释放掉,原因,

  它开启了垃圾回收线程,里面GC:垃圾回收器(回收一些没有更多引用的对象或者变量...)

代码示例

public class Demo {

	public static void main(String[] args) {

		System.out.println("hello");

		new Object();
		new Object();
		new Object();
		new Object();
		new Object();
		// ......n个
		System.out.println("world");

	}
}

如何实现多线程程序呢?
 要实现多线程程序,需要开启进程,
 开启进程,是需要创建系统资源,但是Java语言不能创建系统资源
 只有C/C++可以创建系统资源, 利用c语言创建好的系统资源实现
 Java提供了一个类:Thread类
  实现多线程程序的步骤:
  1)将该类声明为Thread的子类
  2)该子类应重写 Thread类的 run方法
  3)在主线程创建该自定义线程类的对象
 并行和并发(高并发:MyBatis --->IBatis:半自动化)
 前者逻辑上的同时,指的是同一个时间段内
 后者物理上的同时,指的是同一个时间点

 代码示例

public class ThreadDemo {

	public static void main(String[] args) {

		// 创建MyThread类对象
		// MyThread my = new MyThread() ;

		// 当前Thread类有一个run public void run()
		// my.run();
		// System.out.println("-------------------");
		// my.run();
		// 执行线程不是run方法 ,run方法的调用相当于一个普通方法的调用

		/*
		 * public void start()使该线程开始执行;Java虚拟机调用该线程的 run 方法。 结果是两个线程并发地运行
		 */

		MyThread t1 = new MyThread();
		t1.start();
		// IllegalThreadStateException:非法状态异常,同一个线程只能被执行一次
		// t1.start();
		MyThread t2 = new MyThread();
		t2.start();

	}
}
public final void setDaemon(boolean on) :true时,表示为守护线程
  将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。(守护线程不会立即结束掉,它会执行一段时间在结束掉)
  该方法必须在启动线程前调用。

代码示例

package thread;

public class ThreadDemo2 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		myThread.setName("张飞");
		myThread2.setName("关羽");
		myThread.setDaemon(true);// 设置为守护线程
		myThread2.setDaemon(true);// 设置为守护线程
		myThread.start();
		myThread2.start();
		Thread.currentThread().setName("刘备");
		for (int x = 0; x < 5; x++)
			System.out.println(Thread.currentThread().getName());
	}
}
跟线程的优先级相关的方法:
  public final int getPriority()返回线程的优先级。 
  public final void setPriority(int newPriority)更改线程的优先级
  线程存在一个默认优先级
 
 public static final int MAX_PRIORITY 10 最大优先级
 public static final int MIN_PRIORITY 1 最小优先级
 public static final int NORM_PRIORITY 5 默认优先级

代码示例

package thread;

public class ThreadDemo3 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		MyThread myThread3 = new MyThread();
		myThread.setName("林青霞");
		myThread2.setName("王祖贤");
		myThread3.setName("林志颖");
		myThread.setPriority(1);
		myThread2.setPriority(5);
		myThread3.setPriority(10);
		System.out.println(myThread3.getPriority());
		myThread2.start();
		myThread.start();
		myThread3.start();
	}
}
public final void join():等待该线程终止 interruputedException 中断异常

 分别创建三个子线程,让第一个子线程执行之后,调用join()等待该线程终止,再执行t2,t3线程

代码示例

package thread;

public class ThreadDemo4 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		MyThread myThread3 = new MyThread();
		myThread.setName("李渊");
		myThread2.setName("李世民");
		myThread3.setName("李元霸");
		myThread.start();
		try {
			myThread.join();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		myThread2.start();
		myThread3.start();
	}
}

睡眠

Thread.sleep(long millis)Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以减慢线程。当线程睡眠时,它

入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。

线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。 

睡眠的实现:调用静态方法。

        try {
            Thread.sleep(123);
        } catch (InterruptedException e) {
            e.printStackTrace();  
        }

 

睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。

例如,在前面的例子中,将一个耗时的操作改为睡眠,以减慢线程的执行。可以这么写:

    public void run() {
        for(int i = 0;i<5;i++){

// 很耗时的操作,用来减慢线程的执行
//            for(long k= 0; k <100000000;k++);
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();  .
            }

            System.out.println(this.getName()+" :"+i);
        }
    }

public static void sleep(long millis):线程睡眠 指定是时间毫秒值

   throws InterruptedException 
两个区别? public final void stop() ;强迫线程停止执行。 不会执行了 (过时了),方法能使用的
   public void interrupt()中断线程。 表示中断线程的一种状态 面试题 区别? wait(): wait()调用的,立即释放锁 (同步锁/Lock锁)

   sleep(): 线程睡眠,调用不会释放锁

代码示例

package thread;

import java.util.Date;

public class MyThread extends Thread {
	public MyThread() {
		super();
		// TODO Auto-generated constructor stub
	}

	public MyThread(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		// super.run();
		for (int x = 0; x < 50; x++) {
			try {
				Thread.sleep(1000);
				System.out.println(getName() + "的线程睡眠1毫秒");
			} catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			System.out.println(getName() + ":" + x);
		}
	}

}
package thread;

public class ThreadDemo4 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		myThread.setName("李渊");
		myThread2.setName("李世民");
		myThread.start();
		myThread2.start();
	}
}
需求:某电影院出售某些电影票(复联3,红高粱....),有三个窗口同时进行售票(100张票),请您设计一个程序,模拟电影院售票
  两种方式:
  继承

  接口

第一种方式:继承类Thread

代码示例

package org.westos_01;

//SellTicket线程
public class SellTicket extends Thread {

	// 为了不让外界更改这个类中的数据
	// private
	// 定一个票数

	// 为了让每一个线程共同使用一个数据,此数据应该被static修饰
	// private int tickets = 100 ;
	private static int tickets = 100;

	@Override
	public void run() {
		// 为了模拟电影卖票(模拟一直有票)
		// 死循环

		// st1,st2,st3都有执行这个里面的方法
		// st1 100
		// st2 100
		// st3 100
		while (true) {
			if (tickets > 0) {
				System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
			}
		}
	}
}
public class SellTicketDemo {
	
	public static void main(String[] args) {
		
		//创建三个子线程,分别代码三个窗口
		SellTicket st1 = new SellTicket() ;
		SellTicket st2 = new SellTicket() ;
		SellTicket st3 = new SellTicket() ;
		
		//设置线程名称
		st1.setName("窗口1");
		st2.setName("窗口2");
		st3.setName("窗口3");
		
		//启动线程
		st1.start();
		st2.start();
		st3.start();
	}
}

第二种方式  实现接口Runnable

代码示例

package org.westos_02;

public class SellTicket implements Runnable {

	// 定义100张票
	private int tickets = 100;

	@Override
	public void run() {

		while (true) {
			if (tickets > 0) {
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
			}
		}
	}

}
package org.westos_02;

//第二种方式实现

//给继续加一些内容(延迟操作实现)
public class SellTicketDemo {

	public static void main(String[] args) {

		// 创建资源类对象(共享资源类对象/目标对象(target))
		SellTicket st = new SellTicket();
		// 创建线程类对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}

 为了模拟更真实的场景,加入延迟操作(让我们线程睡100毫秒)
  程序的设计是好的,但是结果有一些问题
  1)同一张票被卖了多次
   CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
   2)出现了0或者负票
  (延迟操作+线程执行的随机性)

代码示例

package org.westos_03;

public class SellTicket implements Runnable {
	
	//定义100张票
	private int tickets = 100 ;
/*	@Override
	public void run() {
		
		while(true) {
			
			//t1先进来  t2
			if(tickets>0) {
				//为了模拟更真实的场景(网络售票有延迟的),稍作休息
				try {
					//t1睡 t2睡
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+"正在出售第"+(tickets--)+"张票");
				//100张票
				//为什么会出现同一张票被卖多次?
				*//**
				 * 出现同票的原因:CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
				 * t1线程进来,睡完了,100张票
				 * 原子性操作:记录以前的值
				 * 接着tickets-- :票变成99张票
				 * 在马上输出第99张票的时候,t2/t3进来,直接输出所记录的之前的票数
				 * 出现:
				 * 	窗口1正在出售第100张票
				 * 	窗口3正在出售第99张票
				 * 	窗口2正在出售第99张票
				 *  原子性操作
			}
		}
	}*/
	
	
	@Override
	public void run() {

		while (true) {
			try {
				// t1睡 t2睡
				Thread.sleep(100); // 延迟操作
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			if (tickets > 0) {
				//t1,t2,t3 三个线程执行run里面代码
				//为了模拟更真实的场景(网络售票有延迟的),稍作休息
				
				System.out.println(Thread.currentThread().getName()
						+"正在出售第"+(tickets--)+"张票");//0
				/**
				 * 理想状态:
				 * 		t1正在出售第3张票
				 * 		t3正在出售第2张票
				 * 		t2正在出售第1张票
				 * ...
				 * 		负票
				 * 		t1出售第0张票		(延迟操作+线程执行的随机性)
				 * 		t3正在出售-1张票
				 * 
				 */
			}
		}
	}

}
public class SellTicketDemo {

	public static void main(String[] args) {

		// 创建资源类对象(共享资源类/目标对象)
		SellTicket st = new SellTicket();

		// 创建线程类对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}
通过刚才的这个程序,有安全问题(同票还有负票)
  如何解决多线程的安全问题?
   校验一个多线程程序是否有安全隐患的前提条件:
   1)当前程序是多线程环境
   2)有共享数据
   3)有多条语句对共享数据进行操作
  看当前案例是否有多线程的安全问题:
   1)是否是多线程环境
   2)是否有共享数据
   3)是否有多条语句对共享数据进行操作  是
  现在就需要解决安全问题:
   1)多线程环境 不能解决
   2)对共享数据进行优化 不能解决
   3)在多条语句对共享数据进行操作这一环进行解决
  
   解决方案:就是将多条语句对共享数据操作的代码,用同步代码块包起来
   格式:
   synchronized(锁对象){
   针对多条语句对共享数据操作代码;
  }
  锁对象:肯定一个对象,随便创建一个对象(匿名对象) 
   给刚才的这个程序加入了同步代码块,但是锁对象使用的是匿名对象(每一个线程进来都有自己各自的锁),还是没有解决!

   锁对象:所有线程公用一把锁

代码示例

package demo;

public class SellTicket implements Runnable {
	private int tickets = 100;
	private Object object = new Object();

	@Override
	public void run() {
		while (true) {
			synchronized (object) {
				if (tickets > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
				}
			}
		}
	}
}
package demo;

public class SellTicketDemo {
	public static void main(String[] args) {
		// 创建资源类对象(共享资源类对象/目标对象(target))
		SellTicket sellTicket = new SellTicket();
		// 创建线程类对象
		Thread thread = new Thread(sellTicket, "窗口1");
		Thread thread2 = new Thread(sellTicket, "窗口2");
		Thread thread3 = new Thread(sellTicket, "窗口3");
		// 启动线程
		thread.start();
		thread2.start();
		thread3.start();
	}
}
举例:
   火车上上厕所(锁对象:将它看成是门的开和关) 
   synchronized(锁对象){
多条语句对共享数据进行操作的代码;
  
 注意:
  锁对象:一定要使用同一把锁(所有线程只能公用一把锁)

  锁对象:任何的Java类(引用类型)

代码示例

package org.westos_05;

public class SellTicket implements Runnable {

	// 定义100张票
	private int tickets = 100;

	private Object obj = new Object();
	private Demo d = new Demo();

	@Override
	public void run() {
		while (true) {

			// t1,t2,t3
			synchronized (d) { // 门的开和关

				// t1进来,门会关掉
				// t2进来,门关掉
				// t3进来,门关掉
				if (tickets > 0) {
					try {
						// 0.1
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
					// 窗口1正在出售第100张票
					// 窗口2正在出售第99张票
					// 窗口3正在出售98张票
					// ....

					// 虽然加入延迟操作,但是因为有synchronized,所以不会出现0票或者负票了
				}
			}

		}
	}

}

class Demo {
}
package org.westos_05;

/**
 * 举例:
 * 		火车上上厕所(锁对象:将它看成是门的开和关)
 * 
 * 		synchronized(锁对象){
 * 				多条语句对共享数据进行操作的代码;
 * 		}
 * 
 *注意:
 *		锁对象:一定要使用同一把锁(所有线程只能公用一把锁)
 *		锁对象:任何的Java类(引用类型)
 * 			
 *
 */
public class SellTicketDemo {
	
	public static void main(String[] args) {
		
		//创建资源类对象(共享资源类对象/目标对象)
		SellTicket st = new SellTicket() ;
		
		//创建线程类对象
		Thread t1 = new Thread(st, "窗口1") ;
		Thread t2 = new Thread(st ,"窗口2") ;
		Thread t3 = new Thread(st, "窗口3") ;
		
		//启动线程
		
		t1.start();
		t2.start();
		t3.start();
	}
}

同步方法

当同步方法为静态(static)时

private synchronized static void SellTicket(){}

synchronized(类名.class);

当同步方法为非静态时

private synchronized void SellTicker(){}

synchronized(this);

代码示例

package demo;

public class SellTicket implements Runnable {
	private static int tickets = 100;
	private static int x = 100;
	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				synchronized (this) {
					if (tickets > 0) {
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"+" x:"+x--);
					}
				}
			}
				else {
				sellTicket();
			}
		}
	}
	//静态同步方法
	/*private synchronized static void sellTicket() {
		if (tickets > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"+" x:"+x--);
		}
	}*/
	//非静态同步方法
	private synchronized void sellTicket() {
		if (tickets > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"+" x:"+x--);
		}
	}

}

线程安全的类  StringBuffer

线程安全的集合   

                       单列集合  Vector

                       双列集合  Hashtable

代码示例

package org.westos_06;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

public class ThreadDemo {

	public static void main(String[] args) {

		// 线程安全的类StringBuffer Vector<V> Hashtable<K,V>
		StringBuffer sb = new StringBuffer();
		Vector<String> vector = new Vector<String>();
		Hashtable<String, String> hm = new Hashtable<String, String>();

		// Vector<String>它是线程安全的集合,但是还是不习惯使用这个集合,通过ArrayList集合:线程不安全的类
		List<String> arrayList = new ArrayList<String>(); // 线程不安全的类
		ArrayList<String> arrayList2 = new ArrayList<String>();
		// public static <T> List<T> synchronizedList(List<T> list)
		// 返回指定列表支持的同步(线程安全的)列表
		List list = Collections.synchronizedList(arrayList); // 线程安全的方法
		List list2 = Collections.synchronizedList(arrayList2); // 线程安全的方法
	}
}j

jdk5.0以后,java提供了一个具体的锁: 接口:Lock

private Lock lock=new ReentrantLock();

lock.lock();//显示获取锁

lock.unlock();//释放锁

代码示例

package org.westos_07;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
	// 定义票
	private int tickets = 100;
	// Object obj = new Object();
	// Jdk5.0以后,java提供了一个具体的锁: 接口:Lock
	private Lock lock = new ReentrantLock(); // 显示获取锁的前提,一定要创建Lock接口对象

	@Override
	public void run() {
		while (true) {
			try { // try...finally
				lock.lock(); // 获取锁 syncrhonized(obj)
				if (tickets > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
				}

			} finally {// 释放锁
				if (lock != null) {
					lock.unlock();
				}

			}

		}

	}

}

死锁

简单描述就是比如说有两个锁,第一个锁中包含着第二个锁,第二个锁中也包含着第一个锁,第一个锁执行到其代码块中有第二个锁的时候,它会等第二个锁执行完毕释放掉后才能执行此处的代码,必须等待第二个锁执行完毕,但是线程抢到CPU主动权后执行第二把锁对应的代码块时,执行到其代码块中有第一把锁的地方,必须等第一把锁执行完毕释放掉第一把锁后才会执行此处代码,所以第二把锁在等待第一把锁执行完毕,就这样,第一把锁在等待第二把锁,第二把锁也在等待第一把锁,互相等待啊!!!,就这样一直等啊等,等啊等,就一直不执行,一直等着,就变成了死锁。

代码示例

package org.westos_08;

public class DieLock extends Thread {

	// 声明一个成员变量
	private boolean flag;

	public DieLock(boolean flag) {
		this.flag = flag;
	}

	// 重写run方法
	@Override
	public void run() {
		if (flag) {
			synchronized (MyLock.objA) {
				System.out.println("if ObjA");
				synchronized (MyLock.objB) {
					System.out.println("if objB");
				}
			}
		} else {
			synchronized (MyLock.objB) {
				System.out.println("else objB");
				synchronized (MyLock.objA) {
					System.out.println("else objA");
				}
			}
		}
	}

	/**
	 * 第一种情况: if ObjA else objB
	 * 
	 * 第二种情况 else objB if ObjA
	 * 
	 * 第三种情况: 理想状态 else objB else objA if ObjA if objB
	 * 
	 * if ObjA if objB else objB else objA
	 * 
	 * 
	 */
}
package org.westos_08;

/**
 * 自定义两个锁对象
 *
 */
public class MyLock {

	// 两个锁对象分别是objA 和objB
	public static final Object objA = new Object();
	public static final Object objB = new Object();
}
package org.westos_08;

//测试类

//解决了多线程安全问题,但是还是有些问题:
//1)执行效率低
//2)会产生死锁

//两个或两个以上的线程,在执行的过程中出现互相等待的情况,就叫做死锁!
public class DieLockDemo {

	public static void main(String[] args) {

		// 创建线程了对象
		DieLock dl1 = new DieLock(true);
		DieLock dl2 = new DieLock(false);

		// 启动线程
		dl1.start();
		dl2.start();
	}

}
分析:
   Student类: 资源类
   SetThread:设置学生的数据(生产者线程)
   GetThread:获取(输出)学生数据(消费者线程)
  StudentDemo:测试类 
  需求:SetThread线程给学生对象进行赋值,再通过消费者线程输出该学生数据,设计这样一个程序!
   null----0 按照刚才的思路,发现有一个问题,的数据null---0 
  解决方案:线程死锁的注意事项:要保证生产者线程和消费者线程是针对同一个对象进行操作的!
   在主线程main中创建一个学生对象,将这个学生对象通过构造方法传给每一个线程

代码示例

package org.westos_09;

public class Student {

	String name;
	int age;
}
package org.westos_09;

//生产者线程
public class SetThread implements Runnable {

	private Student student;

	public SetThread(Student s) {
		this.student = s;
	}

	@Override
	public void run() {
		// 设置学生数据
		student.name = "高圆圆";
		student.age = 27;
	}
}
package org.westos_09;

//消费者线程
public class GetThread implements Runnable {
	private Student student;

	public GetThread(Student s) {
		this.student = s;
	}

	@Override
	public void run() {
		// 输出该学生数据
		System.out.println(student.name + "----" + student.age);
	}
}
public class StudentDemo {

	public static void main(String[] args) {

		// 针对同一个对象进行操作
		Student s = new Student();

		// 创建线程类对象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);

		// 创建线程了对象
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);

		// 启动线程
		t1.start();
		t2.start();

	}
}

分析:
   Student类: 资源类
   SetThread:设置学生的数据(生产者线程)
GetThread:获取(输出)学生数据(消费者线程)
  StudentDemo:测试类 
  需求:SetThread线程给学生对象进行赋值,在通过消费者线程输出该学生数据,设计这样一个程序!
   null----0 按照刚才的思路,发现有一个问题,的数据null---0 
  解决方案:线程死锁的注意事项:要保证生产者线程和消费者线程针对同一个对象进行操作的!
   在外部创建一个学生对象,将这个学生对象通过构造方法传入到各个线程中
  需求:消费者线程,和生产者线程加入循环操作,改进
  又有问题:
   1)同一个人的姓名和年龄出现多次
   2)姓名和年龄不符
  为什么?
   1)CPU的一点点时间片,在某一个时间点,足够它执行很多次
   2)线程具有随机性
 解决方案:
  1)是否是多线程环境
  2)是否有共享数据            是
  3)是否有多条语句对共享数据进行操作
  同步机制(同步代码块/同步方法)

  开发中,使用synchronized(Lock锁也可以)同步代码块将多条语句对共享数据的操作包起来!

代码示例


package org.westos_10;

//生产者线程
public class SetThread implements Runnable {

	private Student s;
	// 定义一个变量
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (s) {
				if (x % 2 == 0) {
					s.name = "高圆圆";
					s.age = 27;
				} else {
					s.name = "张杨";
					s.age = 28;
				}
				x++;

			}

		}

	}
}
package org.westos_11;

//消费者线程
public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		// 输出该学生数据
		while (true) {
			synchronized (s) {
				// 如果本身消费者有数据
				if (!s.flag) { // true==!false s.flag==false 说明没有数据 !flag
					try {
						s.wait();// 和网络编程里面的accept()一样 都属于阻塞式方法
						// 消费线程等待,等待生产者线程生产数据

					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} // !s.flag==false s.flag==true 说明有数据,有数据则输出数据
				System.out.println(s.name + "----" + s.age);// 高圆圆---27
				// 输出之后就没有数据了
				s.flag = flase;
				// 通知生产者线程,让其赶紧生产数据
				s.notify(); // 唤醒生产者线程
			}
		}

	}
}
package org.westos_11;

//消费者线程
public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		// 输出该学生数据
		while (true) {
			synchronized (s) {
				// 如果本身消费者有数据
				if (!s.flag) { // true==!false s.flag==false 说明没有数据 !flag
					try {
						s.wait();// 和网络编程里面的accept()一样 都属于阻塞式方法
						// 消费线程等待,等待生产者线程生产数据

					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} // !s.flag==false s.flag==true 说明有数据,有数据则输出数据
				System.out.println(s.name + "----" + s.age);// 高圆圆---27
				// 输出之后就没有数据了
				s.flag = false;
				// 通知生产者线程,让其赶紧生产数据
				s.notify(); // 唤醒单生产者线程
			}
		}

	}
}
public class StudentDemo {

	public static void main(String[] args) {

		// 针对同一个对象进行操作
		Student s = new Student();
		// 创建线程类对象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		// 创建线程类对象
		Thread t1 = new Thread(st); // 生产者
		Thread t2 = new Thread(gt);// 消费者
		// 启动线程
		t1.start();
		t2.start();
	}
}

ThreadGroup 线程组

线程组:表示一个线程的集合。此外,线程组也可以包含其他线程组 

代码示例

package org.westos_12;

/**
 * 线程组:表示一个线程的集合。此外,线程组也可以包含其他线程组
 *
 * 线程池(某个线程执行完毕,反复利用线程对象)
 *
 */
public class ThreadGroupDemo {

	public static void main(String[] args) {

		// 获取线程组的名称
		//
		// method1();

		// 如何给多个线程设置一个线程组名称呢?
		method2();
	}

	private static void method2() {
		// public ThreadGroup(String name)构造一个新线程组
		ThreadGroup tg = new ThreadGroup("main-新的线程组");

		MyThread my = new MyThread();

		// Thread(ThreadGroup group, Runnable target, String name)
		Thread t1 = new Thread(tg, my, "线程1");
		Thread t2 = new Thread(tg, my, "线程2");

		// 直接获取线程组名称
		System.out.println(t1.getThreadGroup().getName());
		System.out.println(t2.getThreadGroup().getName());
	}

	private static void method1() {
		MyThread my = new MyThread();

		// 创建线程类对象

		Thread t1 = new Thread(my, "线程1");
		Thread t2 = new Thread(my, "线程2");

		// public final ThreadGroup getThreadGroup()返回该线程所属的线程组
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();

		// public final String getName():返回线程组的名称
		System.out.println(tg1.getName()); // main
		System.out.println(tg2.getName());// main

		// 所有的线程它默认的线程组名称:main(主线程)
		System.out.println(Thread.currentThread().getThreadGroup().getName());// main
	}
}
package org.westos_12;

public class MyThread implements Runnable {
	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}

	}

}

线程池 ExecutorService

线程池:多个线程执行完毕,它会重新回到线程池中,等待被利用,不会变成垃圾!
  和线程池有关的类
 类 Executors: 一种工厂类
  方法:
  和线程池的创建有关系
  public static ExecutorService newFixedThreadPool(int nThreads)
  创建一个可重用固定线程数的线程池
 
 ExecutorService:可以执行异步任务
  创建一个线程池,执行接口中的方法
  提交:Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
 Future:接口
  Future 表示异步计算的结果
  线程池调用完毕可以关闭的
  void shutdown():关闭之前,会提交刚才的任务

代码示例

public class ExceutorsDemo {

	public static void main(String[] args) {

		// 创建一个线程池
		ExecutorService pool = Executors.newFixedThreadPool(2);// 创建一个线程池中包含了2条线程

		// 提交和Runnable接口的方法或者Callable(提交任务)
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());

		// pool-1-thread-2 :线程池-池数-线程类对象的描述-编号(从1开始)

		// 关闭线程池
		pool.shutdown();

	}
}
package org.westos_13;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}

	}

}

Callable 

call()方法

多线程第三种实现方式:
  前提:自定义类实现Callable接口
  1)创建线程池对象: Executors 里面的那个方法,返回的是ExecutorsService
  2) 然后调用ExecutorsService里面的提交任务的方法:
  <T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行
  3)关闭线程池

代码示例

public class ExecutorsDemo {

	public static void main(String[] args) {

		// 创建线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 提交任务
		pool.submit(new MyCallable());
		pool.submit(new MyCallable());

		// 关闭线程池
		pool.shutdown();
	}

}
package org.westos_14;

import java.util.concurrent.Callable;

//Callable的泛型的类型它是call方法的返回值类型
public class MyCallable implements Callable {

	@Override
	public Object call() throws Exception {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}

		return null;
	}

}

需求:利用线程来计算数字之和

代码示例

package org.westos_15;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 分别计算每个线程的求和!
 *
 */
public class ExecutorsTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		// 创建线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 提交任务
		Future<Integer> f1 = pool.submit(new MyCallable(100));
		Future<Integer> f2 = pool.submit(new MyCallable(200));

		// V get():获取结果
		Integer i1 = f1.get();
		Integer i2 = f2.get();

		System.out.println(i1);
		System.out.println(i2);

		// 关闭线程池
		pool.shutdown();
	}
}
package org.westos_15;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {

	// 定义个变量
	private int number;

	public MyCallable(int number) {
		this.number = number;
	}

	@Override
	public Integer call() throws Exception {
		// 定义最终结果变量
		int sum = 0;
		for (int x = 1; x <= number; x++) {
			sum += x;
		}
		return sum;
	}

}
















猜你喜欢

转载自blog.csdn.net/qq_40727767/article/details/80454733