JAVA并发程序设计第一步:了解Java虚拟机提供的API操作,以及线程基本概念的操作:
1:定义线程 -- 继承Thread类和实现Runnable方法
1 /** 2 * 定义线程 1:继承Thread类,方法、形式如下 3 */ 4 public static class T1 extends Thread{ 5 @Override 6 public void run() { 7 System.out.println("Threads.T1.run()"); 8 } 9 10 public static void main(String[] args) { 11 /** 12 * 启动线程:只需要使用new关键字创建一个线程对象,并且将它 start()起来即可。 13 */ 14 T1 t1 = new T1(); 15 Thread thread = new Thread(t1); 16 thread.start(); 17 T2 t2 = new T2(); 18 Thread thread2 = new Thread(t2); 19 thread2.start(); 20 } 21 } 22 /** 23 *定义线程 实现runnable接口,方法、形式如下 24 */ 25 public static class T2 implements Runnable{ 26 27 public void run() { 28 System.out.println("Threads.t2.run()"); 29 } 30 31 }
2:线程终止-- 不要用 API 提供的 stop() , stop太暴力,应自己写逻辑实现
public static class StopThread implements Runnable{ volatile boolean stopme = false; public void stopeMe(){ stopme = true; } public void run() { while (true) { if (stopme) { System.out.println("exit by stop me"); break; } } } }
3:线程中断
/** * 线程中断 * public void Thread.interrupt() //中断线程 * Public void boolean Thread.isInterrupted() //判断线程是否被中断 * Public static boolean Thread.interrupted() //判断是否被中断,并请除当前中断状态 */ public static class InterrupThread implements Runnable{ public void run() { while(true){ try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Thread running"); } } public static void main(String[] args) { /** * 启动线程:只需要使用new关键字创建一个线程对象,并且将它start()起来即可。 */ InterrupThread interrupThread = new InterrupThread(); Thread thread = new Thread(interrupThread); thread.start(); // Thread.sleep(1000); /** * thread 会 产生中断,但是并没有处理中断,遇到 sleep 抛出异常 * 当程序出现 wait 和sleep 的时候,中断会被忽略掉 * 如果线程运行到了sleep()代码段,主程序中断线程,线程这这时候抛出异常, * 进入catch的异常处理。在catch代码段中,由于捕获到了中断,我们可以立即退出线程。 * 在这里我们并没有这么做,因为也许在这段代码中,我们还必须进行后续处理,保证数据的一致性和完整性, * 因此,执行了Thread.interrupt()方法在次中断自己,设置中断标志位。 */ thread.interrupt(); System.out.println(thread.isInterrupted()); } }
4:等待和通知
public class WaitAndNotify { /** * JDK提供了两个非常重要的接口,线程等待wait()方法和线程通知方法notify()。这两个方法不是在Thread类中的 * object.notifyAll()方法,他会唤醒等待队列中的所有线程。 */ final static Object object = new Object(); public static class MyThread_1 extends Thread{ @Override public void run(){ synchronized (object) { System.out.println(System.currentTimeMillis()+"T1 start"); try { System.out.println(System.currentTimeMillis()+"T1 wait"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+"T1 end"); } } } public static class MyThread_2 extends Thread { @Override public void run() { synchronized (object) { System.out.println(System.currentTimeMillis() + "T2 start and notify"); object.notify(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread t1 = new MyThread_1(); Thread t2 = new MyThread_2(); t1.start(); t2.start(); } }
5:volatile关键字解析
public class VolatileApp { /** * 这里的 ready 和 number 相当于 被 volatile 修饰的变量 * volatile来声明一个变量时,就等于告诉了虚拟机,这个变量极有可能会被某些程序或者是线程修改。 * 为了确保这个变量被修改后,应用程序范围内所有线程都能看到这个改动,虚拟机就必须采取一些特殊的手段,保证这个变量的可见性等特点。 */ private static boolean ready; private static int number; public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start(); thread.sleep(1000); ready = false; number = 100; thread.sleep(2000); } public static class MyThread implements Runnable{ public void run() { while(!ready) System.out.println(number); } } }
6:线程组概念
public class ThreadGroupApp { /** * 构造方法: * * ThreadGroup(String name):以指定线程组名字来创建新线程组 * * ThreadGroup(ThreadGroup parent,String name):以指定的名字、指定的父线程组来创建一个新线程组。 * * 常用操作方法: * * · int activeCount():获取线程组中活动线程的数量 * * · interrupt():中断线程组中所有线程 * * · isDaemon():是否为后台线程组 * * · setDaemon(boolean daemon):设置为后台线程组 * * · setMaxPriority(int pri):设置线程组的最高优先级 * * 线程都是在创造的同时加入线程组中,然后才start。上述代码展示了线程组的两个重要功能, * * activeCount()可以获得活动线程的总数,但由于线程是动态的,所以这个值是一个预估值, * * list()可以打印这个线程组中所有的线程信息,对调试有一定帮助。 */ public static void main(String[] args) { ThreadGroup threadGroup = new ThreadGroup("ThreadGroupApp"); MyThread myThread_1 = new MyThread(threadGroup,"myThread_1"); MyThread myThread_2 = new MyThread(threadGroup,"myThread_2"); myThread_1.setDaemon(true);//设置守护线程 ,必须在start 之前执行 myThread_1.setPriority(1);//设置线程优先级 1-10 myThread_1.start(); myThread_2.start(); System.out.println(threadGroup.activeCount()); threadGroup.list(); } public static class MyThread extends Thread{ public MyThread(String name){ super(name); } public MyThread(ThreadGroup group,String name){ super(group, name); } @Override public void run() { super.run(); } } }
7:synchronized 线程安全概念
/** * 线程安全 synchronized * 除了线程同步,确保线程安全, synchronized 确保线程之间可见、有序 * 从可见性的角度上讲,synchronized完全可以代替volatile的功能,只是使用上没有那么方便。就有序性而言, * 由于synchronized每一次只有一个线程可以访问同步块,因此,无论同步块内的代码如何被乱序执行,只要保证串行语义一致,那么执行的结果总是一样的。 * 换而言之,被synchronized限制的多个线程是串行执行的。 */ public class ThreadSafeApp { public static int number; public static void main(String[] args) throws InterruptedException { AddThread addThread_1 = new AddThread(); AddThread addThread_2 = new AddThread(); Thread thread_1 = new Thread(addThread_1); Thread thread_2 = new Thread(addThread_2); thread_1.start(); thread_2.start(); thread_1.join(); thread_2.join(); // 等待线程结束 System.out.println(number); } public static class AddThread implements Runnable{ /** * 牵扯到内存模型,每一个类 new 的时候会生成方法区,在堆中独立的空间所以声明synchronized 同步方法只是在这个类中构造独立,那么两个内存空间还是相互影响 * 数据不一致 -- 解决办法为:设置成静态 */ public static synchronized void add(){ number++; } public void run() { // synchronized (AddThread.class) { for (int i = 0; i < 10000; i++) { // number++; add(); } } // } } }
8:重入锁
public class LockApp { public static ReentrantLock Lock = new ReentrantLock(); public static int Count = 0; public static void main(String[] args) throws InterruptedException { ThreadLock t1 = new ThreadLock(); ThreadLock t2 = new ThreadLock(); t1.start();t2.start(); t1.join();t2.join(); System.out.println(Count); } public static class ThreadLock extends Thread{ @Override public void run() { for(int i=0;i<10000;i++) { /** * 必须手动指定何时加锁 ,何时释放锁。也正是因为这样, * 重入锁逻辑控制远远要好于synchronized。 * 但值得注意的是,在退出零界区时,必须记得要释放锁,否者永远没有机会再访问零界区了,会造成其线程的饥饿甚至是死锁。 * 针对 lock() 可以多次调用! 但是必须释放 */ Lock.lock(); Count++; Lock.unlock(); } } } }
9:lockInterruptibly 重入锁的中断
/** * 重入锁的中断 lockInterruptibly * 优先考虑响应中断,而不是响应锁的普通获取或重入获取。 */ public class ThreadInterruptibilyApp { public static ReentrantLock Lock1 = new ReentrantLock(); public static ReentrantLock Lock2 = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { MyThread t1 = new MyThread(1); MyThread t2 = new MyThread(2); t1.start(); t2.start(); Thread.sleep(3000); t2.interrupt(); /** * 在t1和t2线程start后,主线程main进入休眠,此时t1和t2线程处于死锁状态, * 然后主线程main中断t2线程, * 故t2会放弃对lock1的请求,同时释放lock2。这个操作使得t1可以获得lock2从而继续执行下去。 * 那么,完成工作的只有t1 ,t2 是直接放弃推出,释放资源 */ } public static class MyThread extends Thread{ int flag; MyThread(int flag) { this.flag = flag; } @Override public void run() { try{ if(flag == 1){ try { Lock1.lockInterruptibly(); Thread.sleep(1000); Lock2.lockInterruptibly(); System.out.println(flag+"号线程:完成工作"); } catch (InterruptedException e) {} } else if(flag == 2){ try { Lock2.lockInterruptibly(); Thread.sleep(1000); Lock1.lockInterruptibly(); System.out.println(flag+"号线程:完成工作"); } catch (InterruptedException e) {} } }finally{ //中断响应 if(Lock1.isHeldByCurrentThread()){ Lock1.unlock(); System.out.println(flag+":Lock1 interrupted unlock"); } if(Lock2.isHeldByCurrentThread()){ Lock2.unlock(); System.out.println(flag+":Lock2 interrupted unlock"); } System.out.println(flag+"号线程退出"); } } } }
10:Map、ArrayList、Vector、ConcurrenHashMap 线程安全测试
public class ThreadErrorApp { public static Map<String,String> threadMap = new HashMap<String, String>(); public static ArrayList<Integer> threadArrayList = new ArrayList<Integer>(); public static Vector<Integer> threadVector = new Vector<Integer>(); public static void main(String[] args) throws InterruptedException { // MyThread myThread_1 = new MyThread(); // MyThread myThread_2 = new MyThread(); // Thread thread_1 = new Thread(myThread_1); // Thread thread_2 = new Thread(myThread_2); // thread_1.start(); // thread_2.start(); // thread_1.join(); // thread_2.join(); // System.out.println(threadVector.size()); MyThreadMap myThread_1 = new MyThreadMap(); MyThreadMap myThread_2 = new MyThreadMap(); Thread thread_1 = new Thread(myThread_1); Thread thread_2 = new Thread(myThread_2); thread_1.start(); thread_2.start(); thread_1.join(); thread_2.join(); System.out.println(threadMap.size()); } /** * 1: 报错java.lang.ArrayIndexOutOfBoundsException 因为内存一致性 遭到破坏,产生越界问题 * 2:size 小于 20000 * 解决办法 使用Vector */ public static class MyThread extends Thread { @Override public void run() { for (int i=0;i<10000;i++){ // threadArrayList.add(i); threadVector.add(i); } } } /** * 程序正常结束,但结果不符合预期,而是一个小于100000的数字。 * 使用 ConcurrenHashMap 代替 */ public static class MyThreadMap extends Thread{ @Override public void run() { for (int i=0;i<10000;i++){ threadMap.put(Integer.toString(i),Integer.toString(i)); } } } }