Java——单例模式、多线程


单例模式

  • 单例模式是一种常用的软件设计模式。

  • 在它的核心结构中只包含一个被成为单例类的特殊类。通过单例模式可以保证系统中一个类职业一个实例而且该实例易于外界访问,从而方面对实例个数的控制并节约系统资源。

  • 如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

单例模式练习

public class Person {

    private int id;
    //单例模式:整个系统共享一个对象
    public Person(int id) {
        super();
        this.id = id;
    }
    //测试
    public static void main(String[] args) {
        Person p1 = new Person(1);
        Person p2 = new Person(2);
    }
    public void test(){
        Singleton ton = Singleton.getInstance();
        System.out.println(ton.hashCode());
    }
}

单例模式的分类

懒汉式

  • 优点:只有调用方法才创建对象,调用之前不会占用内存

  • 缺点:在多线程模式下不安全

懒汉式相关练习:

public class Singleton {
    //私有化构造,只有在本类之中才可以new出来。超出本类无法访问
    private Singleton(){
    }
    //全局对象
    private static Singleton singleton=null;
    //synchronized  如果加上关键字,则一样
    // synchronized 如果没有该关键字,则创建了两个线程不同步
    //synchronized 它的位置决定了锁的范围  
    public static synchronized Singleton getInstance(){
        //判断全局对象是否为空
        if(singleton==null){
            try {
                //休眠一秒钟
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //如果为空,就创建该类对象
            singleton=new Singleton();
        }
        //如果不为空,就直接返回该对象
        return singleton;
    }
    public static void main(String[] args) {
        //多线程操作
        //2.直接使用Thread操作
        Thread thread = new Thread(){
            @Override
            public void run() {
                // TODO Auto-generated method stub
                Singleton ton = Singleton.getInstance();
                System.out.println(ton.hashCode());
            }
        };
        thread.start();
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                // TODO Auto-generated method stub
                Singleton ton2 = Singleton.getInstance();
                System.out.println(ton2.hashCode());
            }
        };
        //开启新线程
        thread2.start();
//      Person p = new Person(0);
//      p.test();
//      Singleton s = new Singleton();
//      System.out.println(s.hashCode());
    }
}

饿汉式

  • 跟懒汉式的区别,直接创建对象。

  • 饿汉式不管有没有调用getInstance方法,都会预先在系统中创建一个静态对象

  • 懒汉式不会预先创建对象,只有在第一次调用时才创建对象。

  • 优点:在多线程模式下是安全的

  • 缺点:没有调用方法前就加载,会占用系统内存。

饿汉式相关练习:

public class Singleton2 {
    //跟懒汉式的区别,直接创建对象
    private static Singleton2 ton = new Singleton2();
    //私有构造
    private Singleton2(){
        System.out.println("对象创建成功!");
    }
    public static Singleton2 getInstance(){
        return ton;
    }
    /**
     * 饿汉式不管有没有调用getInstance方法,
     * 都会预先在系统中创建一个静态对象。
     * 懒汉式不会预先创建对象,只有在第一次调用时,
     * 才创建对象。
     * 饿汉式
     * 优点:在多线程模式下是安全的。
     * 缺点:没有调用方法前就加载,会占用系统内存。
     * @param args
     */
    public static void main(String[] args) {

        Singleton2 ton = Singleton2.getInstance();
        System.out.println(ton.hashCode());
    }

}

线程安全

  • 饿汉式本身就是线程安全的,可以直接用于多线程而不会出现问题的。

  • 懒汉式是非线程安全的,解决方式如下:

关键字:synchronized:可以用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这个段代码。

使用双重检测机制实现线程安全的懒汉式

这里写图片描述

使用静态内部类实现线程安全的单例模式

这里写图片描述


多线程

public class Ch01 {
    /**
     * 系统默认情况下只运行主线程
     * @param args
     *
    public static void main(String[] args) {
        //主线程中开启两个子线程
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();
    }
}
/**
 * 多线程类1
 * @author alery
 *
 */
class Thread1 extends Thread{
    /**
     * 线程运行期间执行的代码
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("线程1开始运行。。。。。");
        //休眠1秒钟
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("线程1 继续执行......");
    }
}

/**
 * 多线程类2
 */
class Thread2 extends Thread{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("线程2开始运行。。。。。");
        //休眠1秒钟
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("线程2 继续执行......");
    }
}

多线程的三种方式

import java.util.ArrayList;
import java.util.List;
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;
import java.util.concurrent.FutureTask;

/**
 * JAVA三种方式实现多线程
 * @author Administrator
 *
 */
public class ThreadTest implements Runnable,Callable<String>{

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // TODO Auto-generated method stub
        //主线程打印
//      System.out.println("线程编号:"+Thread.currentThread().getId());
//      new ThreadA().start();
//      Thread thread=new Thread(){
//          public void run() {
//              System.out.println(Thread.currentThread().getId()+"线程正在运行");
//          }
//      };
//      thread.start();
        ThreadTest t=new ThreadTest();
//      new Thread(t).start();
//      new Thread(new Runnable() {
//          
//          @Override
//          public void run() {
//              // TODO Auto-generated method stub
//              System.out.println(Thread.currentThread().getId()+"线程正在运行");
//          }
//      }).start();
        //Runnable
//      FutureTask<String> task=new FutureTask<String>(t);
//      new Thread(task).start();
//      //获取call方法返回的内容
//      System.out.println(task.get());
        //线程池内线程最大数量
        int threadMaxCount=10;
        //线程池
        ExecutorService service=Executors.newFixedThreadPool(threadMaxCount);
        //定义一个集合存储runnable
        List<Future<String>> list=new ArrayList<Future<String>>();
        //开始运行10个线程(这10个多线程是由线程池管理的)
        for(int i=0;i<threadMaxCount;i++) {
            FutureTask<String> task=new FutureTask<String>(t);
            //submit把runnable提交给了线程池
            Future<String> f=(Future<String>) service.submit(task);
            list.add(task);
        }

//      service.shutdown();

        for(int i=0;i<list.size();i++) {
            Future<String> f=list.get(i);
            System.out.println(f.get());
        }
    }


    /**
     * 2.实现Runnable接口
     * 必须重写run方法
     * 创建Thread对象,把runnable传递到构造中
     * (匿名实现类)
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println(Thread.currentThread().getId()+"线程正在运行");
    }

    /**
     * 3.实现Callable接口
     * 重写call方法
     * 使用FutureTask类来实现Runnable
     * (FutureTask是Runnable的实现类)
     * 创建Thread对象,把FutureTask当作Runnable调用了构造参数
     * call方法的返回值可以通过FutureTask.get方法得到
     * (get方法必须在线程运行之后才可以调用)
     * 线程池配合使用比较好
     */
    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        String str=Thread.currentThread().getId()+"线程正在运行";
//      System.out.println(str);
        return str;
    }

}
/**
 * 1.继承Thread类
 * 重写run方法
 * (匿名类方式实现)
 * @author Administrator
 *
 */
class ThreadA extends Thread{

    /**
     * 当线程start时,会执行run
     */
    @Override
    public void run() {
//      super.run();
        // TODO Auto-generated method stub
        System.out.println(Thread.currentThread().getId()+"线程正在运行");
    }

}
原文地址:https://www.cnblogs.com/aixing/p/13327725.html