Java 进阶知识(五)

5 阅读8分钟
package thread;
/**
 * 线程是并发运行代码的。
 * 有两种创建线程的方式:
 * 方式一,继承Thread并重写run方法。run方法中
 * 就是希望线程执行的逻辑。
 * @author ta
 *
 */
public class ThreadDemo1 {
	public static void main(String[] args) {
		Thread t1 = new MyThread1();
		Thread t2 = new MyThread2();
		/*
		 * 启动线程要调用start方法,而不是
		 * 直接调用run方法。当start方法调用
		 * 完毕后,run方法很快会被线程自行调
		 * 用。
		 */
		t1.start();
		t2.start();
	}
}
/**
 * 第一种创建线程的方式比较简单直接,但是缺点
 * 主要有两个:
 * 1:由于需要继承线程,这导致不能再继承其他类
 *   实际开发中经常要复用某个超类的功能,那么
 *   在继承线程后不能再继承其他类会有很多不便
 * 
 * 2:定义线程类的同时重写了run方法,这会导致
 *   线程与线程要执行的任务有一个必然的耦合
 *   关系,不利于线程的重用。
 * @author ta
 *
 */
class MyThread1 extends Thread{
	public void run() {
		for(int i=0;i<1000;i++) {
			System.out.println("你是谁啊?");
		}
	}
}

class MyThread2 extends Thread{
	public void run() {
		for(int i=0;i<1000;i++) {
			System.out.println("我是查水表的!");
		}
	}
}
package thread;
/**
 * 第二种创建线程的方式
 * 实现Runnable接口,单独定义线程任务
 * @author ta
 *
 */
public class ThreadDemo2 {
	public static void main(String[] args) {
		//实例化两个任务
		Runnable r1 = new MyRunnable1();
		Runnable r2 = new MyRunnable2();
		//创建两个线程并指派任务
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		
		t1.start();
		t2.start();
	}
}

class MyRunnable1 implements Runnable{
	public void run() {
		for(int i=0;i<1000;i++) {
			System.out.println("你是谁啊?");
		}		
	}
}

class MyRunnable2 implements Runnable{
	public void run() {
		for(int i=0;i<1000;i++) {
			System.out.println("我是查水表的!");
		}		
	}
}
package thread;
/**
 * 线程提供了获取相关信息的方法
 * @author ta
 *
 */
public class ThreadDemo3 {
	public static void main(String[] args) {
		/*
		 * 线程提供了一个静态方法:
		 * static Thread currentThread()
		 * 该方法用来获取运行这个方法的线程
		 * main方法也是靠一个线程运行的,当JVM启动
		 * 后会自动创建一个线程来执行main方法。而
		 * 这个线程的名字叫做"main",我们称它为主线程
		 * 
		 * 获取运行main方法的线程
		 * 
		 */
		Thread main = Thread.currentThread();
		System.out.println("运行main方法的线程:"+main);
		
		dosome();
		
		Thread t = new Thread() {
			public void run() {
				Thread t = Thread.currentThread();
				System.out.println("自定义线程:"+t);
				dosome();
			}
		};
		t.start();
	}
	
	public static void dosome() {
		Thread t = Thread.currentThread();
		System.out.println("运行dosome方法的线程是:"+t);
	}
	
}
package thread;
/**
 * 线程提供了获取自身信息的相关方法
 * @author ta
 *
 */
public class ThreadDemo4 {
	public static void main(String[] args) {
		Thread main = Thread.currentThread();
		//获去线程的名字
		String name = main.getName();
		System.out.println("name:"+name);
		//获去线程的唯一标识(id)
		long id = main.getId();
		System.out.println("id:"+id);
		//获去线程的优先级
		int priority = main.getPriority();
		System.out.println("优先级:"+priority);
		//线程是否还处于活动状态
		boolean isAlive = main.isAlive();
		System.out.println("isAlive:"+isAlive);
		//线程是否是被中断了
		boolean isInterrupted = main.isInterrupted();
		System.out.println("isInterrupted:"+isInterrupted);
		//线程是否为守护线程
		boolean isDaemon = main.isDaemon();
		System.out.println("isDaemon:"+isDaemon);
	}
}
package thread;
/**
 * 线程的优先级
 * 线程不能主动获去CPU时间片,只能被动的被
 * 线程调度器分配。
 * 调整线程的优先级可以最大程度的改善某个线程
 * 获去CPU时间片的次数。
 * 理论上线程优先级约高的线程获去CPU时间片的
 * 次数就越多。
 * @author ta
 *
 */
public class PriorityDemo {
	public static void main(String[] args) {
		Thread max = new Thread() {
			public void run() {
				for(int i=0;i<10000;i++) {
					System.out.println("max");
				}
			}
		};
		Thread min = new Thread() {
			public void run() {
				for(int i=0;i<10000;i++) {
					System.out.println("min");
				}
			}
		};
		Thread norm = new Thread() {
			public void run() {
				for(int i=0;i<10000;i++) {
					System.out.println("nor");
				}
			}
		};
		
		max.setPriority(Thread.MAX_PRIORITY);
		min.setPriority(Thread.MIN_PRIORITY);		
		min.start();
		norm.start();
		max.start();
	}
}
package thread;
/**
 * 线程提供了一个静态方法:
 * static void sleep(long ms)
 * 使运行这个方法的线程阻塞指定毫秒。超时后
 * 该线程会自动回到RUNNABLE状态,等待再次并发
 * 运行。
 * @author ta
 *
 */
public class SleepDemo {
	public static void main(String[] args) {
		System.out.println("程序开始了");
		while(true) {
			System.out.println("你好!");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	
	}
}
package thread;

import java.util.Scanner;

/**
 * 倒计时程序
 * 程序启动后,要求输入一个数字,比如:100
 * 然后每秒输出一次,每次输出数字递减。
 * 输出到0时提示结束,程序退出。
 * @author ta
 *
 */
public class Test {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入一个数字:");
		String line = scanner.nextLine();		
		Integer num = Integer.parseInt(line);
		for(;num>0;num--) {
			System.out.println(num);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("结束!");
	}
}
package thread;
/**
 * sleep方法要求必须处理中断异常,原因在于当
 * 一个线程调用了sleep方法处于阻塞状态的过程
 * 中若被调用了它的interrupt()方法中断时,它
 * 就会在sleep方法中抛出中断异常。这时并非是
 * 将这个线程直接中断,而是中断了它的阻塞状态
 * @author ta
 *
 */
public class SleepDemo2 {
	public static void main(String[] args) {
		/*
		 * JDK8之前,由于JVM内存分配问题,有一个
		 * 要求:
		 * 当一个方法的局部变量被这个方法的其他
		 * 局部内部类所引用是,这个变量声明必须
		 * 是final的。
		 */
		final Thread lin = new Thread() {
			public void run() {
				System.out.println("林:刚美完容,睡一会吧!");
				try {
					Thread.sleep(1000000);
				} catch (InterruptedException e) {
					System.out.println("林:干嘛呢!干嘛呢!干嘛呢!都破了相了!");
				}
				System.out.println("林:醒了!");
			}
		};
		
		Thread huang = new Thread() {
			public void run() {
				System.out.println("黄:开始砸墙!");
				for(int i=0;i<5;i++) {
					System.out.println("黄:80!");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
					}
				}
				System.out.println("咣当!");
				System.out.println("黄:搞定!");
				//中断lin线程
				lin.interrupt();
			}
		};		
		lin.start();
		huang.start();
	}
}
package thread;
/**
 * 线程提供了一个方法:
 * void join()
 * 该方法可以协调线程之间的同步运行
 * 
 * 同步与异步:
 * 同步运行:运行有顺序
 * 异步运行:运行代码无顺序,多线程并发运行就
 *          是异步运行
 * @author ta
 *
 */
public class JoinDemo {
	//标识图片是否下载完毕
	private static boolean isFinish = false;
	
	public static void main(String[] args) {
		Thread download = new Thread() {
			public void run() {
				System.out.println("down:开始下载图片...");
				for(int i=1;i<=100;i++) {
					System.out.println("down:"+i+"%");
					try {
						Thread.sleep(20);
					} catch (InterruptedException e) {
					}
				}
				System.out.println("down:下载图片完毕!");
				isFinish = true;
			}
		};
		
		Thread show = new Thread() {
			public void run() {
				System.out.println("show:开始显示图片");
				//加载图片前应先等待下载线程将图片下载完毕	
				try {
					/*
					 * show线程在调用download.join()方法后
					 * 就进入了阻塞状态,直到download线程
					 * 的run方法执行完毕才会解除阻塞
					 */
					download.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				if(!isFinish) {
					throw new RuntimeException("加载图片失败!");
				}
				System.out.println("show:显示图片完毕!");
			}
		};
		download.start();
		show.start();
	}
}
package thread;
/**
 * 多线程并发的安全问题。
 * 产生:当多个线程并发操作同一资源时,由于线程
 * 切换实际的不确定性,会导致执行操作资源的代码
 * 顺序未按照设计顺序执行,出现操作混乱的情况。
 * 严重时可能导致系统瘫痪。
 * 
 * 解决:将并发操作同一资源改为同步操作,即:有
 *      先后顺序的操作。
 * @author ta
 *
 */
public class SyncDemo {
	public static void main(String[] args) {
		Table table = new Table();
		Thread t1 = new Thread() {
			public void run() {
				while(true) {
					int bean = table.getBean();
					Thread.yield();
					System.out.println(getName()+":"+bean);
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				while(true) {
					int bean = table.getBean();
					Thread.yield();
					System.out.println(getName()+":"+bean);
				}
			}
		};
		t1.start();
		t2.start();
	}
}

class Table{
	//20个豆子
	private int beans = 20;	
	/**
	 * 当一个方法被synchronized修饰后,该方法
	 * 称为"同步方法",即:多个线程不能同时在方
	 * 法内部运行。强制让多个线程在执行同一个
	 * 方法时变为同步操作就解决了并发安全问题
	 * 
	 * 在方法上使用synchronized,那么同步监视器
	 * 对象就是当前方法所属对象,即:方法内部看到
	 * 的this
	 * @return
	 */
	public synchronized int getBean() {
		if(beans==0) {
			throw new RuntimeException("没有豆子了!");
		}
		//模拟线程执行到这里没有时间了
		Thread.yield();
		return beans--;
	}	
}
package thread;
/**
 * 同步块
 * 语法:
 * synchronized(同步监视器对象){
 *    需要同步运行的代码片段
 * }
 * 
 * 同步块可以更精确的控制需要同步运行的代码
 * 片段。有效的缩小同步范围可以在保证并发安全
 * 的前提下提高代码并发运行的效率
 * 使用同步块控制多线程同步运行必须要求这些
 * 线程看到的同步监视器对象为[同一个]。
 * 
 * @author ta
 *
 */
public class SyncDemo2 {
	public static void main(String[] args) {
		Shop shop = new Shop();
		Thread t1 = new Thread() {
			public void run() {
				shop.buy();
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				shop.buy();
			}
		};
		t1.start();
		t2.start();
	}
}

class Shop{
//	public synchronized void buy() {
	public void buy() {
		try {
			Thread t = Thread.currentThread();
			System.out.println(t.getName()+":正在挑衣服...");
			Thread.sleep(5000);
			synchronized (this) {
				System.out.println(t.getName()+":正在试衣服...");
				Thread.sleep(5000);
			}
			
			System.out.println(t.getName()+":结账离开");
		} catch (Exception e) {
			
		}
	}
}
package thread;
/**
 * 静态方法若使用synchroinzed修饰,那么该方法
 * 一定具有同步效果
 * 静态方法对应的同步监视器对象为当前类的
 * 类对象(Class的实例)。类对象会在后面反射
 * 的课程中讲到。
 * @author ta
 *
 */
public class SyncDemo3 {
	public static void main(String[] args) {
		Thread t1 = new Thread() {
			public void run() {
				Foo.dosome();
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				Foo.dosome();
			}
		};
		t1.start();
		t2.start();
	}
}

class Foo{
	public synchronized static void dosome() {
		Thread t = Thread.currentThread();
		System.out.println(t.getName()+":正在运行dosome方法");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(t.getName()+":运行dosome方法完毕");
	}
	
}
package thread;
/**
 * 互斥锁
 * 当多个代码片段被synchronized块修饰后,这些
 * 同步块的同步监听器对象又是同一个时,这些
 * 代码片段就是互斥的。多个线程不能同时在这些
 * 方法中运行。
 * @author ta
 *
 */
public class SyncDemo4 {
	public static void main(String[] args) {
		Boo boo = new Boo();
		Thread t1 = new Thread() {
			public void run() {
				boo.methodA();
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				boo.methodB();
			}
		};
		t1.start();
		t2.start();
	}
}

class Boo{
	public synchronized void methodA() {
		Thread t = Thread.currentThread();
		try {
			System.out.println(t.getName()+":正在运行A方法");
			Thread.sleep(5000);
			System.out.println(t.getName()+":运行A方法完毕");
		} catch (Exception e) {
		}
	}
        
	public void methodB() {
		synchronized (this) {
			Thread t = Thread.currentThread();
			try {
				System.out.println(t.getName()+":正在运行B方法");
				Thread.sleep(5000);
				System.out.println(t.getName()+":运行B方法完毕");
			} catch (Exception e) {
			}
		}
	}
}
package threadpool;

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

/**
 * 线程池
 * 线程池主要有两个作用:
 * 1:控制线程数量
 * 2:重用线程
 * @author ta
 *
 */
public class ThreadPoolDemo {
	public static void main(String[] args) {
		ExecutorService threadPool = Executors.newFixedThreadPool(2);
		
		for(int i=0;i<5;i++) {
			Runnable runn = new Runnable() {
				public void run() {
					Thread t = Thread.currentThread();
					try {
						System.out.println(t.getName()+":正在运行任务...");
						Thread.sleep(5000);
						System.out.println(t.getName()+":运行任务完毕");
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			};
			//将任务指派给线程池
			threadPool.execute(runn);
			System.out.println("指派了一个任务给线程池");
		}//for循环结束
		
		//停止线程池
		/*
		 * shutdown()方法调用后,线程池不再
		 * 接收新任务,并且会将线程池中所有
		 * 的任务执行后自动停止
		 * 
		 * shutdownNow()方法调用后,线程池
		 * 会强制中断所有线程立即停止
		 * 
		 */
//		threadPool.shutdown();
		threadPool.shutdownNow();
		System.out.println("线程池停止了!");
	}
}