JavaSE——day18线程&day19多线程问题、线程组、线程池

JVM:JAVA虚拟机识别main 方法(主线程)。

JVM是多线程的,至少有2个线程。

要实现多线程程序需要开启进程。而开启进程需要创建系统资源,但java语言不能创建系统资源,只有c/c++可以。java中提供一个类:Thread类。

    Java实现多线程程序的步骤:

        1)将类声明为Thread的子类

        2)该类重写Thread中的run()方法

        3)在主线程进行该自定义线程类的对象创建

例子(子类继承Thread类并重写run方法):

public class Test extends Thread{

	@Override
	public void run() {
		//每个线程都需要执行这里的代码,想写啥写啥
		//一般写的是IO操作、循环语句或者一些费时操作
		super.run();
	}
}
public class ThreadDemo {
	
	public static void main(String[] args) {
		MyThread t1 = new MyThread() ;
		t1.start();
		MyThread t2 = new MyThread() ;
		t2.start();
	}
}

执行上面程序两次可以得到不同的结果,可以看到多线程是抢占资源的。

Thread 类提供了一些方法:

  public final void setName(String name):给线程起名称

  public final String getName() :获取线程名称

注意:这些方法都是被final修饰的!!!

static Thread currentThread()
          返回对当前正在执行的线程对象的引用。
 void setDaemon(boolean on)
          将该线程标记为守护线程或用户线程。

形式参数为true时表示为启动守护线程。守护线程必须在启动线程前调用。守护线程在被守护线程执行完后不会立即死去,而是执行一段时间后才死去。

代码:

package thread;
/**
 * 守护线程
 * @author malaganguo
 *
 */
public class Test2 {

	public static void main(String[] args) {
		
		//创建两个子线程
		ThreadDemo td1 = new ThreadDemo() ;
		ThreadDemo td2 = new ThreadDemo() ;
		
		//线程命名
		td1.setName("守护线程1");
		td2.setName("守护线程2");
		
		//创建守护线程
		td1.setDaemon(true);
		td2.setDaemon(true);
		
		td1.start();
		td2.start();
		
		//被守护的主线程命名
		Thread.currentThread().setName("被守护线程");
		for(int x = 0 ;x < 5 ;x++) {
			System.out.println(Thread.currentThread().getName()+":"+x);
		}
	}
}

//创建一个线程类
class ThreadDemo extends Thread {

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

结果:

貌似首先执行的一定是被守护的线程。

线程优先级:线程优先级默认为5,即大家的优先级都是相同的,我们也可以设置优先级,下面是设置线程优先级的3个字段:

 public static final int MAX_PRIORITY 10 最大优先级
 public static final int MIN_PRIORITY 1         最小优先级
 public static final int NORM_PRIORITY 5 默认优先级

与线程优先级相关的两个方法:

    public final int getPriority()返回线程的优先级。 

    public final void setPriority(int newPriority)更改线程的优先级。    优先级越大,被抢先执行的概率越大。

public final void join():等待该线程终止 。编译时期异常interruputedException 中断异常,try catch解决

package thread;
/**
 * 等待线程终止
 * 设置优先级
 * @author malaganguo
 *
 */
public class ThreadJoinTest {


	public static void main(String[] args) {
		//创建3个对象
		ThreadJoin tj1 = new ThreadJoin();
		ThreadJoin tj2 = new ThreadJoin();
		ThreadJoin tj3 = new ThreadJoin();
		//命名
		tj1.setName("先执行者");
		tj2.setName("竞争者1");
		tj3.setName("竞争者2");
		//先执行优先执行者
		tj1.start();
		try {
			tj1.join();//等待tj1线程执行完毕
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//设置优先级
		tj2.setPriority(10);
		tj3.setPriority(1);
		//执行竞争者
		tj2.start();
		tj3.start();
		
	}
}
static void sleep(long millis)
          在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)
          在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

public final void stop() ;强迫线程停止执行。 不会执行了 (过时了),方法能使用

public void interrupt()中断线程。 表示中断线程的一种状态 

 void wait()
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
package thread;

import java.util.Date;

class ThreadSleep extends Thread {

	@Override
	public void run() {
		super.run();
		for (int x = 0; x < 20; x++) {
			System.out.println(getName() + ":" + x + ",日期是:" + new Date());

			// 休眠1000毫秒
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class ThreadSleepDemo {
	public static void main(String[] args) {
		// 创建两个子线程
		ThreadSleep ts1 = new ThreadSleep();
		ThreadSleep ts2 = new ThreadSleep();
		// 起名字
		ts1.setName("进程1");
		ts2.setName("进程2");

		ts1.start();
		ts2.start();
	}
}

public static void yield()暂停当前正在执行的线程对象,并执行其他线程


多线程中遇到的问题

    举个栗子:我们假设一个线程是一个售票窗口,我们创建三个线程,这三个线程负责来销售100张电影票。

售票类代码:

class SeilTicket extends Thread {
	
	private int ticket = 100 ;
	
	@Override 
	public void run() {
		
		while(true) {
			
			//加入线程睡眠,模拟延迟
			try {
				Thread.sleep(100);//延迟时间越长,对CPU原子性操作的抵抗作用越强
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(ticket > 0 ) {
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票" );
			}
		}
	}
}

创建3个售票对象作为窗口 进行售票的代码:

//创建3个资源对象
 		SeilTicket st1 = new SeilTicket() ;
		SeilTicket st2 = new SeilTicket() ;
		SeilTicket st3 = new SeilTicket() ;
		
		st1.setName("窗口1"); 
		st2.setName("窗口2");
		st3.setName("窗口3");
		
		st1.start(); 
		st2.start();
		st3.start();

这样售票的结果:


现在创建一个资源类,给他分配3个线程:

//创建1个资源类对象
		SeilTicket st = new SeilTicket() ;
		//分配3个新的线程对象
		Thread th1 = new Thread(st, "线程1") ;
		Thread th2 = new Thread(st, "线程2") ;
		Thread th3 = new Thread(st, "线程3") ;
		
		th1.start();
		th2.start();
		th3.start();

这样售票的结果:


可以看到由于CPU的原子性还是会售出重复的票,并没有得到解决。

这属于多线程安全问题!

多线程安全问题的隐患条件?

    1)是否是多线程环境

    2)是否有共享数据

    3)是否有多条语句对共享数据进行操作


如果上面都是true,那么即存在多线程不安全的问题。为了解决,引入

同步代码块和同步锁

  
同步代码块格式:

    synchronized (锁对象) {

            //这里是针对多条语句对共享数据操作的代码

    }

Demo:

	@Override 
	public void run() {
		
		while(true) {
			
			//加入锁对象
			synchronized (this) {
				
				//加入线程睡眠,模拟延迟
				try {
					Thread.sleep(100);//延迟时间越长,对CPU原子性操作的抵抗作用越强
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if(ticket > 0 ) {
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票" );
				}
				
			}
		
		}
	}
这里加了锁对象,解决了线程安全问题

    这里所对象可以是任意一个Object ,当然也可以用this关键字代替 ,如果锁对象是一个静态同步的方法,那么需所对象应该为:方法名.class


	private synchronized static  void sellTicket() {//这是一个静态同步方法
		if(tickets>0) {
			try {
				Thread.sleep(100); 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+"正在出售第"+(tickets--)+"张票");
		}
	}

如果使用这个方法作为锁的话,那么我们使用同步代码块的格式是:

     synchronized (sellTicket.class){

             //这里是针对多条语句对共享数据操作的代码

    }

注意:每一个线程只能使用同一把锁(同一个对象)!!!

同步锁(重入锁)

    Lock 是一个接口,这个接口中有一个子实现类:ReentrantLock  。我们创建同步锁的对象使用这个类来创建锁对象。在具体的加锁解锁我们使用try{}finally{if(lock != null){lock.unlock() ;}}这个格式来处理。

例子:

	@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();
				}
				
			}

		}

	}

死锁

    这是解决线程安全问题使用锁后容易出现的问题。死锁的概念是:两个或两个以上的线程,在执行的过程中出现互相等待的情况,就叫做死锁。

例子:

public class Test2 {

	public static void main(String[] args) {
		
		DieLock dl1 = new DieLock(true) ;
		DieLock dl2 = new DieLock(false) ;
		
		dl1.start();
		dl2.start();
	}
}

/**
 * 创建两把锁对象
 * @author malaganguo
 *
 */
class lockObject {
	
	public static final Object lockobj1 = new Object() ;
	public static final Object lockobj2 = new Object() ;
}

class DieLock extends Thread {
	
	
	
	private boolean flag ;
	
	public DieLock(boolean flag) {
		this.flag = flag ;
	}
	
	@Override
	public void run() {
		
		if(flag) {
			synchronized (lockObject.lockobj1) {
				System.out.println("if obj1");
				synchronized (lockObject.lockobj2) {
					System.out.println("if obj2");
				}
			}
		}else {
			synchronized (lockObject.lockobj2) {
				System.out.println("else obj2");
				synchronized (lockObject.lockobj1) {
					System.out.println("else obj1");
				}
			}
		}
	}
}

这样运行的话由于死锁的存在,可能会输出的结果组合:

1)if obj1           else obj2

2)  if obj2           else obj1

3)  这种可能很小很小   if obj1        ifobj2        else obj2           else obj1

4)  这种可能很小很小   else obj2           else obj1        if obj1        ifobj2        

    

模拟生产消费者模式

        两个线程:生产者,消费者

            立一个flag,这个flag默认为false,这个false的意思是默认没有产品供消费者使用,所以先执行生产者,生产者执行完毕后,将flag改为true,这个true的意思是产品已经被生产出来了,告诉消费者该消费了。代码如下:

/***
 * 生产者消费者模式
 * flag为true执行消费状态,为false执行生产状态
 * @author malaganguo
 *
 */
public class Test3 {

	public static void main(String[] args) {

		// 针对同一个对象进行操作 
		Student s = new Student();
		System.out.println(s.flag);//可以看到这里执行的结果是false  ,即Boolean 的默认值是false,所以首先是消费者等待,唤醒生产者
		// 创建线程对象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);

		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);

		t1.start();
		t2.start();
	}
}

// 创建一个类
class Student {
	private String hasData;
	private String setData;

	public String getHasData() {
		return hasData;
	}

	public void setHasData(String hasData) {
		this.hasData = hasData;
	}

	public String getSetData() {
		return setData;
	}

	public void setSetData(String setData) {
		this.setData = setData;
	}

	boolean flag;// true:默认有数据 false:默认没有数据
}

// 生产者
class SetThread extends Thread {

	private Student s;

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

	// 定义一个变量这个变量可以自加,我们把这个变量的奇偶来输出不同的数据
	private int x = 0;

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

			synchronized (s) {
				if (s.flag) {//如果有数据
					try {
						s.wait();//生产者等待
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				if (x % 2 == 0) {
					s.setHasData("没有数据");
					s.setSetData("设置的第一个Data");
				} else {
					s.setHasData("还是没有数据");
					s.setSetData("设置的第二个Data");
				}

				x++;

				s.flag = true; // 生产出数据
				s.notify(); // 唤醒消费者
			}
		}
	}
}

class GetThread extends Thread {

	private Student s;

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

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

			synchronized (s) {
				if (!s.flag) {//如果没有数据
					try {
						s.wait();//消费者等待
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				System.out.println(s.getHasData());  //消费
				System.out.println(s.getSetData());  //消费

				s.flag = false;  //消费完了,没有数据了

				s.notify();      //唤醒生产者
			}
		}
	}
}

ThreadGroup线程组

    线程组表示一个线程的集合,线程组中还可以有其他的线程组。

package day19.Thread1;

public class ThreadGroupDemo {



	public static void main(String[] args) {

		
		Method1();
		
		Method2();
		
	}

	/**
	 * 这个方法得到的线程名称是当前线程的名称
	 */
	private static void Method2() {
		MyThread my = new MyThread() ;
		
		Thread t1 = new Thread(my, "线程1") ;
		Thread t2 = new Thread(my, "线程2") ;
		
		ThreadGroup tg1 = t1.getThreadGroup() ; 
		ThreadGroup tg2 = t2.getThreadGroup() ;
		//返回该线程组的名称-----都为main
		System.out.println(tg1.getName());
		System.out.println(tg2.getName());
		//范围当前线程组的名称
		System.out.println(Thread.currentThread().getThreadGroup().getName());
	}

	/**
	 * 这个方法得到的是构建一个新的线程组
	 */
	private static void Method1() {
		ThreadGroup tg = new ThreadGroup("main——新的线程组");
		
		//创建连个线程对象
		MyThread mt = new MyThread() ;
		
		Thread t1 = new Thread(tg, mt, "线程1") ;
		Thread t2 = new Thread(tg, mt, "线程2") ;
		
		//获取线程名字
		System.out.println(t1.getThreadGroup().getName());
		System.out.println(t2.getThreadGroup().getName());
	}

}

//创建一个线程类:这个类输出1-100
class MyThread extends Thread {
	
	@Override
	public void run() {
		for(int x = 1 ; x <= 100 ; x ++ ) {
			System.out.println(Thread.currentThread().getName()+":"+x);
		}
	}
}

线程池

    普通的线程在start()调用完毕,JVM将run()方法调用完毕,线程对象会变成垃圾,等待垃圾回收器回收。

而线程池的特点是:1)节约成本    2)线程执行完成后不会变成垃圾,重新回到线程池中,等待被利用。

Demo:

package day19.Thread1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 线程池
 * 1)创建线程池对象,指定线程池的线程数
 * 2)往线程池中加入线程
 * 3)关闭线程池
 * 
 * Executors是一个工厂类
 * public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
 * 
 * 
 * ExecutorService:这是一个接口,可以执行异步任务
 * 提交:Future<?> submit(Runnable task)
 *			<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
 *
 *void shutdown():关闭之前,会提交刚才的任务
 *void shutdownNow():试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
 *
 * @author malaganguo
 */
public class ThreadPool {

	public static void main(String[] args) {
		
		ExecutorService pool = Executors.newFixedThreadPool(3) ;
		
		pool.submit(new MyThread()) ;
		pool.submit(new MyThread()) ;
		pool.submit(new MyThread()) ;
		
		pool.shutdown();
	}
}

class MyRunnable implements Runnable {

	@Override
	public void run() {

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


Demo2:

package day19.Thread1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 创建线程池
 * 
 * 1)自定义类实现Callable接口
 * 2)创建线程池对象
 * 3)调用ExecutorService接口中的submit方法提交任务
 * 4)关闭线程池
 * @author malaganguo
 *
 */
public class ThreadPoolCallable {

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		
		ExecutorService pool =Executors.newFixedThreadPool(2) ;
		
		pool.submit(new MyCallable()) ;
		pool.submit(new MyCallable()) ;
		
		pool.shutdown();
		
	}
}

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;
	}
	
}

Demo3:

package day19.Thread1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
 * 创建线程池的方法
 * 带泛型的
 * 可以完成泛型的运算等
 * 因为Executors是工厂类,得到的是类对象,需要调用get()方法获取值
 * 
 * @author malaganguo
 *
 */
public class ThreadPoolCallableT {

	

	public static void main(String[] args) {
		
		//创建线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(2);
		//提交任务,返回的是一个类(因为Executors是一个工厂类)
		Future<Integer> sum1 = pool.submit(new MyCallable_T(3));
		Future<Integer> sum2 = pool.submit(new MyCallable_T(200));
		
		try {
			//传值  get()方法用来获取结果
			Integer s1 = sum1.get() ;
			Integer s2 = sum2.get() ;
			//打印结果
			System.out.println(s1);
			System.out.println(s2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		
		
	}
}

//带泛型的Callable实现类
class MyCallable_T implements Callable<Integer> {

	private int number ;
	
	public MyCallable_T(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/weixin_38930706/article/details/80446508