多线程的继续理解和一些安全问题和设计模式

来源:互联网 发布:删除数据库下所有表 编辑:程序博客网 时间:2024/06/03 21:42


1.单例设计模式

* 单例设计模式:保证类在内存中只有一个对象。 *如何保证类在内存中只有一个对象呢?(1)控制类的创建,不让其他类来创建本类的对象。private (2)在本类中定义一个本类的对象。private static/final(3)提供公共的访问方式。  public static ****(){return s}* 单例主要写法两种设计模式,还有一种无名.

public class a {/** * @param args * 单例设计模式:保证类在内存中只有一个对象。 */public static void main(String[] args) {// TODO Auto-generated method stub//Singleton s1 = new Singleton();Singleton s1 = Singleton.s;//成员变量被私有,不能通过类名.调用//Singleton.s = null;//禁止成员变量被修改,private修饰Singleton s2 = Singleton.s;System.out.println(s1 == s2);//true/*Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);*/}}/** * 饿汉式 * **//*class Singleton {//1,私有构造方法,其他类不能访问该构造方法了private Singleton(){}//2,创建本类对象//public static Singleton s = new Singleton();private static Singleton s = new Singleton();//3,对外提供公共的访问方法public static Singleton getInstance() {//获取实例return s;}}*//** * 懒汉式,单例的延迟加载模式,开发时一般不用,面试用,多线程访问时有安全隐患 * **//*class Singleton {//1,私有构造方法,其他类不能访问该构造方法了private Singleton(){}//2,声明一个引用private static Singleton s ;//3,对外提供公共的访问方法public static Singleton getInstance() {//获取实例if(s == null) {//什么时候用什么时候创建//线程1等待,线程2等待s = new Singleton();}return s;}}*///第三种,无名class Singleton {//1,私有构造方法,其他类不能访问该构造方法了private Singleton(){}//2,声明一个引用public static final Singleton s = new Singleton();//final是最终的意思,被final修饰的变量不可以被更改}


饿汉式和懒汉式的区别* 1,饿汉式是空间换时间,懒汉式是时间换空间* 2,在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象


2.单例设计模式饿汉式的应用——Runtime


Runtime

java.lang.Object——java.lang.Runtimepublic class Runtimeextends Object每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。 应用程序不能创建自己的 Runtime 类实例。 

public Process exec(String command) throws IOException在单独的进程中执行指定的字符串命令。 对于 exec(command) 形式的调用而言,其行为与调用 exec(command, null, null) 完全相同。 参数:command - 一条指定的系统命令。 返回:一个新的 Process 对象,用于管理子进程 


public class b {/** * @param args * @throws IOException  */public static void main(String[] args) throws IOException {Runtime r = Runtime.getRuntime();//获取运行时对象//r.exec("shutdown -s -t 300");//win7,dos,cmd,命令测试 ,5min后关机r.exec("shutdown -a");}}



3.类 Timer

java.lang.Object——java.util.Timerpublic class Timer extends Object一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。 对 Timer 对象最后的引用完成后,并且 所有未处理的任务都已执行完成后,计时器的任务执行线程会正常终止(并且成为垃圾回收的对象)。但是这可能要很长时间后才发生。默认情况下,任务执行线程并不作为守护线程 来运行,所以它能够阻止应用程序终止。如果调用者想要快速终止计时器的任务执行线程,那么调用者应该调用计时器的 cancel 方法。 

如果意外终止了计时器的任务执行线程,例如调用了它的 stop 方法,那么所有以后对该计时器安排任务的尝试都将导致 IllegalStateException,就好像调用了计时器的 cancel 方法一样。 

实现注意事项:所有构造方法都启动计时器线程。 

public void schedule(TimerTask task, Date time)安排在指定的时间执行指定的任务。如果此时间已过去,则安排立即执行该任务。 

public void schedule(TimerTask task, Date firstTime,long period)安排指定的任务在指定的时间开始进行重复的固定延迟执行。以近似固定的时间间隔(由指定的周期分隔)进行后续执行。 

3.1类 TimerTask
java.lang.Object——java.util.TimerTask  所有已实现的接口: Runnable public abstract class TimerTask extends Object implements Runnable由 Timer 安排为一次执行或重复执行的任务。

protected TimerTask()创建一个新的计时器任务。 public abstract void run()此计时器任务要执行的操作。 public boolean cancel()取消此计时器任务。

3.2类 Date
java.lang.Object——java.util.Date所有已实现的接口: Serializable, Cloneable, Comparable<Date> 直接已知子类: Date, Time, Timestamp public class Date extends Objectimplements Serializable, Cloneable, Comparable<Date>类 Date 表示特定的瞬间,精确到毫秒。 

在类 Date 所有可以接受或返回年、月、日期、小时、分钟和秒值的方法中,将使用下面的表示形式: 年份 y 由整数 y - 1900 表示。 月份由从 0 至 11 的整数表示;0 是一月、1 是二月等等;因此 11 是十二月。 日期(一月中的某天)按通常方式由整数 1 至 31 表示。 小时由从 0 至 23 的整数表示。因此,从午夜到 1 a.m. 的时间是 0 点,从中午到 1 p.m. 的时间是 12 点。 分钟按通常方式由 0 至 59 的整数表示。 由 0 至 61 的整数表示;值 60 和 61 只对闰秒发生,尽管那样,也只用在实际正确跟踪闰秒的 Java 实现中。


public class c {/** * @param args * @throws InterruptedException  */public static void main(String[] args) throws InterruptedException {// TODO Auto-generated method stubTimer t = new Timer();//在指定时间安排指定任务//第一个参数,是安排的任务,第二个参数是执行的时间,第三个参数是过多长时间再重复执行t.schedule(new Mytimetask(), new Date(117, 4, 17, 11, 02, 50),3000);while(true) {Thread.sleep(1000);System.out.println(new Date());}}}class Mytimetask extends TimerTask{@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println("起床背英语单词");}}



4.两个线程间的通信

 先说Object的几个方法

public final void wait() throws InterruptedException在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待

public final void notify() 唤醒在此对象监视器上等待的单个线程。 如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。 选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

public final void notifyAll()唤醒在此对象监视器上等待的所有线程。
             

1.什么时候需要通信* 多个线程并发执行时, 在默认情况下CPU是随机切换线程的* 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印2.怎么通信* 如果希望线程等待, 就调用wait()* 如果希望唤醒等待的线程, 就调用notify();* 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用


public class d {/** * @param args * 等待唤醒机制 */public static void main(String[] args) {final Printer p = new Printer();new Thread() {public void run() {while(true) {try {p.print1();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {public void run() {while(true) {try {p.print2();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}}//等待唤醒机制class Printer {private int flag = 1;public void print1() throws InterruptedException {synchronized(this) {if(flag != 1) {this.wait();//当前线程等待}System.out.print("我");System.out.print("是");System.out.print("路");System.out.print("人");System.out.print("甲");System.out.print("\r\n");flag = 2;this.notify();//随机唤醒单个等待的线程}}public void print2() throws InterruptedException {synchronized(this) {if(flag != 2) {this.wait();}System.out.print("倩");System.out.print("女");System.out.print("幽");System.out.print("魂");System.out.print("\r\n");flag = 1;this.notify();}}}



5.三个或三个以上间的线程通信

* 多个线程通信的问题

* notify()方法是随机唤醒一个线程* notifyAll()方法是唤醒所有线程* JDK1.5之前无法唤醒指定的一个线程* 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件


public class e {/** * @param args * 等待唤醒机制 */public static void main(String[] args) {final Printer p = new Printer();new Thread() {public void run() {while(true) {try {p.print1();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {public void run() {while(true) {try {p.print2();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {public void run() {while(true) {try {p.print3();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}}//等待唤醒机制class Printer {private int flag = 1;public void print1() throws InterruptedException {synchronized(this) {if(flag != 1) {this.wait();//当前线程等待}System.out.print("我");System.out.print("是");System.out.print("路");System.out.print("人");System.out.print("甲");System.out.print("\r\n");flag = 2;//this.notify();//随机唤醒单个等待的线程this.notifyAll();}}public void print2() throws InterruptedException {synchronized(this) {if(flag != 2) {this.wait();}System.out.print("倩");System.out.print("女");System.out.print("幽");System.out.print("魂");System.out.print("\r\n");flag = 3;//this.notify();this.notifyAll();}}public void print3() throws InterruptedException {synchronized(this) {if(flag != 3) {this.wait();//线程3在此等待,if语句是在哪里等待,就在哪里起来//如果改成while循环,每次都会判断标记,程序有可能死掉,都等待//while + notifyAll唤醒所有线程解决,1.5版本之间}System.out.print("易");System.out.print("飞");System.out.print("扬");System.out.print("飯");System.out.print("\r\n");flag = 1;//this.notify();this.notifyAll();}}}

标题4、5总结

1,在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法2,为什么wait方法和notify方法定义在Object这类中? * 因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中3,sleep方法和wait方法的区别? * a,sleep方法必须传入参数,参数就是时间,时间到了自动醒来 *   wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待 * b,sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡 *  wait方法在同步函数或者同步代码块中,释放锁,否则,cou一直在此等待,谁都无法执行




6.JDK1.5的新特性互斥锁


ReentrantLock

java.lang.Object——java.util.concurrent.locks.ReentrantLock所有已实现的接口: Serializable, Lock public class ReentrantLock extends Object implements Lock, Serializable一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 


public void lock()获取锁。 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态,此时锁保持计数被设置为 1。

public void unlock()试图释放此锁。 如果当前线程是此锁所有者,则将保持计数减 1。如果保持计数现在为 0,则释放该锁。

public Condition newCondition()返回用来与此 Lock 实例一起使用的 Condition 实例。 


接口 Condition(条件,情况的意思)

java.util.concurrent.locks Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 。

***理解要点*****其中,ReentrantLock替代了 synchronized同步代码块和方法的使用,Condition 替代了 Object 监视器 wait,notify,notifyA方法的使用。****
对于上面这两各类,网上看了很多讲解,讲的很复杂深入,却不容易让人理解。其实抓住这两点替代关系就很容易明了

 

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。


void await() throws InterruptedException造成当前线程在接到信号或被中断之前一直处于等待状态。 


void signal()唤醒一个等待线程。(可以唤醒指定条件的线程) 如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。 


简述

1.同步* 使用ReentrantLock类的lock()和unlock()方法进行同步2.通信* 使用ReentrantLock类的newCondition()方法可以获取Condition对象* 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法* 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了



public class f {/** * @param args */public static void main(String[] args) {final Printer p = new Printer();new Thread() {public void run() {while(true) {try {p.print1();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {public void run() {while(true) {try {p.print2();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {public void run() {while(true) {try {p.print3();} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}}class Printer {private ReentrantLock r = new ReentrantLock();//初始化可重入锁private Condition c1 = r.newCondition();//创建一个锁的条件c1private Condition c2 = r.newCondition();private Condition c3 = r.newCondition();private int flag = 1;public void print1() throws InterruptedException {r.lock();//获取锁。if(flag != 1) {c1.await();//c1条件的锁线程等待}System.out.print("我");System.out.print("是");System.out.print("路");System.out.print("人");System.out.print("甲");System.out.print("\r\n");flag = 2;c2.signal(); //唤醒c2条件的线程,使程序从c2.await()处继续执行//Condition条件的意思,但他替代的是Object线程监视器的作用r.unlock();  //试图释放此锁。}public void print2() throws InterruptedException {r.lock();if(flag != 2) {c2.await();}System.out.print("倩");System.out.print("女");System.out.print("幽");System.out.print("魂");System.out.print("\r\n");flag = 3;c3.signal();r.unlock();}public void print3() throws InterruptedException {r.lock();if(flag != 3) {c3.await();}System.out.print("易");System.out.print("飞");System.out.print("扬");System.out.print("飯");System.out.print("\r\n");flag = 1;c1.signal();r.unlock();}}





7.线程组的概述和使用

* A:线程组概述* Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。* 默认情况下,所有的线程都属于主线程组。* public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组* public final String getName()//通过线程组对象获取他组的名字* 我们也可以给线程设置分组* 1,ThreadGroup(String name) //创建线程组对象并给其赋值名字* 2,创建线程对象* 3,Thread(ThreadGroup?group, Runnable?target, String?name) * 4,设置整组的优先级或者守护线程B:案例演示


public class g {/** * @param args */public static void main(String[] args) {/*MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr, "张三");Thread t2 = new Thread(mr, "李四");ThreadGroup tg1 = t1.getThreadGroup();ThreadGroup tg2 = t2.getThreadGroup();System.out.println(tg1.getName());//默认的是主线程组System.out.println(tg2.getName());//main// 通过结果我们知道了:线程默认情况下属于main线程组// 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组System.out.println(Thread.currentThread().getThreadGroup().getName());*//** * 自己设定线程组 *  */ThreadGroup tg = new ThreadGroup("我是一个新的线程组");//创建新的线程组MyRunnable mr = new MyRunnable();//创建Runnable的子类对象Thread t1 = new Thread(tg, mr, "张三");//将线程t1放在组tg中Thread t2 = new Thread(tg, mr, "李四");//将线程t2放在组tg中System.out.println(t1.getThreadGroup().getName());//获取组名System.out.println(t2.getThreadGroup().getName());//我是一个新的线程组//通过组名称设置后台线程,表示该组的线程都是后台线程tg.setDaemon(true);//tg设置为守护线程组}}class MyRunnable implements Runnable {@Overridepublic void run() {for(int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName() + "...." + i);}}}


8.线程的五种状态

新建,就绪,运行,阻塞,死亡



9.线程池的概述和使用


 A:线程池概述

* 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。* 而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。* 线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。* 在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

 B:内置线程池的使用概述

* JDK1.5新增了一个Executors工厂类来产生线程池,有如下几个方法* 使用步骤:* 创建线程池对象* 创建Runnable实例* 提交Runnable实例* 关闭线程池* 使用步骤:* 创建线程池对象* 创建Runnable实例* 提交Runnable实例* 关闭线程池



Executors

java.lang.Object——java.util.concurrent.Executorspublic class Executors extends Object此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。从以下版本开始: 1.5 


public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池返回:新创建的线程池 


public static ExecutorService newSingleThreadExecutor()创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

这些方法的返回值是ExecutorService对象,


接口 ExecutorService

所有超级接口: Executor 所有已知子接口: ScheduledExecutorService 所有已知实现类: AbstractExecutorService, ScheduledThreadPoolExecutor, ThreadPoolExecutor public interface ExecutorService extends Executor该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程

void shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。 

Future<?> submit(Runnable task)提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。 参数:task - 要提交的任务 返回:表示任务等待完成的 Future 

<T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。


接口 Future<V>

类型参数:V - 此 Future 的 get 方法所返回的结果类型Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。


V get() throws InterruptedException, ExecutionException如有必要,等待计算完成,然后获取其结果。 


接口 Callable<V>

类型参数:V - call 方法的结果类型返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。 


V call() throws Exception计算结果,如果无法计算结果,则抛出一个异常


案例一

public class i {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池pool.submit(new MyRunnable());//将线程放进池子里并执行pool.submit(new MyRunnable());pool.shutdown();//关闭线程池}}class MyRunnable implements Runnable {@Overridepublic void run() {for(int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName() + "...." + i);}}}


案例二

多线程程序实现的方式3提交的是Callable好处和弊端* 好处:* 可以有返回值* 可以抛出异常* 弊端:* 代码比较复杂,所以一般不用



public class j {/** * 多线程程序实现的方式3 * @param args * @throws ExecutionException  * @throws InterruptedException  */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(50));System.out.println(f1.get());System.out.println(f2.get());pool.shutdown();//关闭线程池}}class MyCallable implements Callable<Integer> {private int num;public MyCallable(int num) {this.num = num;}@Overridepublic Integer call() throws Exception {int sum = 0;for(int i = 1; i <= num; i++) {sum += i;}return sum;}}








原创粉丝点击