黑马程序员—多线程

------- android培训java培训、期待与您交流! ----------
多线程概述:

进程:

正在运行的程序,是系统进行资源分配和调用的独立单位。

每一个进程都有它自己的内存空间和系统资源。

线程:

是进程中的单个顺序控制流,是一条执行路径

一个进程如果只有一条执行路径,则称为单线程程序。

一个进程如果有多条执行路径,则称为多线程程序。

 

进程: 我们的操作系统 是一个多进程的操作系统,多进程的目的是为了提高CPU的使用率

多线程: 提高应用程序的使用率

 

 Thread: 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程

  创建线程有两种方式:

   1: 继承 Thread线程类

   a:创建自定义类 继承 Thread类。

   b:重写Thread类中run方法。

   c:通过子类,完成线程对象的创建。

   d:调用start()完成线程对象的启动。

   2:实现Runnable接口

  

  

   start() 与 run() 区别?

  

   public void run(): 每个线程要执行的操作代码。

   public void start(): 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 

  

   run()直接调用,普通方法的使用,没什么意义。

   start(),启动一个线程,并调用该线程对象中的run方法。

  

   IllegalThreadStateException: 非法线程状态异常:

   产生这个异常的原因: 当前的线程已经启动,又一次启动该线程、

   多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

 

public class ThreadDemo {

public static void main(String[] args) {

//c:通过子类,完成线程对象的创建

MyThread my = new MyThread();

MyThread my2 = new MyThread();

//d:调用start()完成线程对象的启动

my.start();

//my.start();

my2.start();

//my.run();

//my2.run();

}

}

//a:创建自定义类 继承 Thread类

public class MyThread extends Thread {

 

//b:重写Thread类中run方法

@Override

public void run() {

System.out.println("我的线程 启动了");

for (int i = 0; i < 100; i++) {

System.out.println(i);

}

}

}

  Thread类中的方法:

   public final String getName() 返回该线程的名称。 

   Thread- + 编号

   编号从0开始, 依次递增

  

   t1 -- Thread-0

   t2 -- Thread-1

  

  

   public final void setName(String name)改变线程名称,使之与参数 name 相同。

  

   public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 

public class ThreadName {

public static void main(String[] args) {

//如何获取正在运行的主线程的名字

System.out.println(Thread.currentThread().getName());

//创建线程对象

MyThread t1 = new MyThread();

MyThread t2 = new MyThread();

//设置线程对象名字的方法

t1.setName("线程一");

t2.setName("线程二");

//启动线程

t1.start();

t2.start();

}

}

public class MyThread extends Thread {

@Override

public void run() {

//每一个线程对象要运行的代码

for (int i = 0; i < 100; i++) {

System.out.println(getName() +"-----"+ i);

}

}

}

创建线程对象的两种方式:

   方式1: 继承Thread类

    

   方式2: 实现Runnable接口 

    a: 自定义类 实现Runnable接口

    b: 实现接口中的run()方法

   c: 创建自定义类对象

    d: 通过Thread类 , 创建线程对象,把自定义类对象,作为构造方法的参数传入使用

    e: 调用start()方法

public class ThreadDemo {

public static void main(String[] args) {

//c: 创建自定义类对象

MyRunnable my = new MyRunnable();

//创建线程对象

//Thread t1 = new Thread(my);

Thread t1 = new Thread(my, "李阳");

Thread t2 = new Thread(my, "李刚");

//启动线程

t1.start();

t2.start();

}

}

//a: 自定义类 实现Runnable接口

public class MyRunnable implements Runnable {

//b: 实现接口中的run()方法

@Override

public void run() {

for (int i = 0; i < 100; i++) {

//System.out.println(getName() +"---"+ i);

System.out.println(Thread.currentThread().getName() +"---"+ i);

}

}

 

}

 

 后台线程

public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出

public class DaemonDemo {

public static void main(String[] args) {

//创建线程对象

DaemonThread t1 = new DaemonThread("守护线程一");

DaemonThread t2 = new DaemonThread("守护线程二");

//设置为后台线程,也理解是守护线程    该方法必须在启动线程前调用。

t1.setDaemon(true);

t2.setDaemon(true);

//启动线程

t1.start();

t2.start();

//主线程,是刘备

Thread.currentThread().setName("主线程");

for (int i = 0; i < 10; i++) {

System.out.println(Thread.currentThread().getName() + "--" + i);

}

}

}

public class DaemonThread extends Thread {

 

public DaemonThread() {

super();

// TODO Auto-generated constructor stub

}

 

public DaemonThread(String name) {

super(name);

// TODO Auto-generated constructor stub

}

@Override

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + "---" + i);

}

}

}

 中断线程

public final void stop() 停止

结束当前正在运行的线程

public void interrupt() 中断

InterruptedException: 

中断当前正在运行的线程, 抛出一个中断异常,而运行还可以运行

当线程在活动之前或活动期间处于正在等待、休眠或占用状态且该线程被中断时,抛出该异常

public class InterruptDemo {

public static void main(String[] args) {

//创建线程对象

InterruptThread t1 = new InterruptThread("线程一");

//启动线程

t1.start();

//将当前正在运行的线程10秒后,中断

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//t1.stop();//停止

try {

t1.interrupt();

} catch (Exception e) {

e.printStackTrace();

}

System.out.println("over");

}

}

 

public class InterruptThread extends Thread {

 

public InterruptThread() {

super();

// TODO Auto-generated constructor stub

}

 

public InterruptThread(String name) {

super(name);

// TODO Auto-generated constructor stub

}

@Override

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + "---" + i);

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

 

线程加入

public final void join()  等待该线程终止,当该线程结束后,才会执行其他线程

public class JoinDemo {

public static void main(String[] args) {

//创建线程对象

JoinThread t1 = new JoinThread("线程一");

JoinThread t2 = new JoinThread("线程二");

JoinThread t3 = new JoinThread("线程三");

//启动线程

t1.start();

try {

t1.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

t2.start();

t3.start();

}

}

 

public class JoinThread extends Thread {

 

public JoinThread() {

super();

// TODO Auto-generated constructor stub

}

 

public JoinThread(String name) {

super(name);

// TODO Auto-generated constructor stub

}

@Override

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + "---" + i);

}

}

}

package cn.itcast_03_ThreadMethod;

  线程的优先级:

  

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

  public final void setPriority(int newPriority)更改线程的优先级。 

   IllegalArgumentException: 错误的参数异常

   IllegalArgumentException - 如果优先级不在 MIN_PRIORITY 到 MAX_PRIORITY 范围内

  

 public static final int MAX_PRIORITY 10  最大优先级

 public static final int MIN_PRIORITY 1 最小优先级 

 public static final int NORM_PRIORITY 5 默认优先级

  

  创建线程对象的构造方法:

   public Thread()

   public Thread(String name)分配新的 Thread 对象

public class PriorityDemo {

public static void main(String[] args) {

//创建线程

PriorityThread t1 = new PriorityThread("线程一");

PriorityThread t2 = new PriorityThread("线程二");

PriorityThread t3 = new PriorityThread("线程三");

//设置优先级

t2.setPriority(10);

t1.setPriority(1);

System.out.println("线程一" + t1.getPriority());

System.out.println("线程二" + t2.getPriority());

System.out.println("线程三" + t3.getPriority());

//启动线程

t1.start();

t2.start();

t3.start();

}

}

 

public class PriorityThread extends Thread {

public PriorityThread() {

super();

}

 

public PriorityThread(String name) {

super(name);

}

 

@Override

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + "---" + i);

}

}

}

 

 线程休眠

public static void sleep(long millis) 让线程对象 休眠给定的毫秒值,当时间到后,继续运行

 

public class SleepDemo {

public static void main(String[] args) {

//创建线程对象

SleepThread t1 = new SleepThread("线程一");

SleepThread t2 = new SleepThread("线程二");

SleepThread t3 = new SleepThread("线程三");

//启动线程

t1.start();

//t2.start();

t3.start();

}

}

public class SleepThread extends Thread {

public SleepThread() {

super();

// TODO Auto-generated constructor stub

}

 

public SleepThread(String name) {

super(name);

// TODO Auto-generated constructor stub

}

@Override

public void run() {

for (int i = 0; i < 99; i++) {

//休息一秒再执行打印操作

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(getName() + "---" + i);

}

}

}

线程礼让

public static void yield()

 

public class YieldDemo {

public static void main(String[] args) {

//创建线程对象

YieldThread t1 = new YieldThread("线程一");

YieldThread t2 = new YieldThread("线程二");

YieldThread t3 = new YieldThread("线程三");

//启动线程

t1.start();

t2.start();

t3.start();

}

}

public class YieldThread extends Thread {

 

public YieldThread() {

super();

// TODO Auto-generated constructor stub

}

 

public YieldThread(String name) {

super(name);

// TODO Auto-generated constructor stub

}

@Override

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + "---" + i);

//谦让一下,让其他线程运行一会,我再运行

Thread.yield();

}

}

}

//同步机制  synchronized()

首先想为什么出现问题?(也是我们判断是否有问题的标准)

是否是多线程环境

是否有共享数据

是否有多条语句操作共享数据

如何解决多线程安全问题呢?

基本思想:让程序没有安全问题的环境。

怎么实现呢?

把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。

 

 

java中为了解决上述的线程问题,提供了一个锁机制, 也叫 同步机制

方式1:同步代码块

格式:

synchronized (对象){

多条语句操作共享数据

}

注意: 多个线程对象,使用的锁对象要是同一个对象

//同步代码块案例:

public class TicketThread {

public static void main(String[] args) {

//模拟3个窗口, 卖票

//创建自定义类对象

Ticket ticket = new Ticket();

//创建线程对象,把自定义类对象 作为构造方法的参数

Thread t1 = new Thread(ticket, "窗口1");

Thread t2 = new Thread(ticket, "窗口2");

Thread t3 = new Thread(ticket, "窗口3");

//启动线程

t1.start();

t2.start();

t3.start();

}

}

public class Ticket implements Runnable {

int ticket = 100;

 

//定义一个锁对象, 这个对象可以是任意的

Object obj = new Object();

@Override

public void run() {

while (true) {

//同步代码块

synchronized (obj){//正确的

//synchronized (new Object()){//错误的

if (ticket>0) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"正在卖票" + ticket--);

}

}

}

}

}

方式二: 同步方法 

方式2: 同步方法

格式:

修饰符 synchronized 返回值类型 方法名(){...}

public synchronized void method(){...}

同步方法的锁对象 是谁呢?

this对象

静态同步方法的锁对象 是谁呢?

类名.class    

public class TicketThread {

public static void main(String[] args) {

//模拟3个窗口, 卖票

//创建自定义类对象

Ticket ticket = new Ticket();

//创建线程对象,把自定义类对象 作为构造方法的参数

Thread t1 = new Thread(ticket, "窗口1");

Thread t2 = new Thread(ticket, "窗口2");

Thread t3 = new Thread(ticket, "窗口3");

//启动线程

t1.start();

t2.start();

t3.start();

}

}

public class Ticket implements Runnable {

static int ticket = 100;

 

//定义一个锁对象, 这个对象可以是任意的

Object objA = new Object();

Object objB = new Object();

// this

// 类名.class 产生一个字节码文件对象  ---getClass()

int x = 0;

@Override

public void run() {

while (true) {

if (x %2 == 0) {

//同步方法 

//method();

//静态同步方法

method2();

} else {

//同步代码块

//synchronized (objA){

//synchronized (objB){

//synchronized (this){

//synchronized (super){

synchronized (Ticket.class){

//synchronized (new Object()){//错误的

if (ticket>0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"正在卖票" + ticket--);

}

}

}

x++;

}

}

 

//静态同步方法

public static synchronized void method2() {

synchronized (Ticket.class){//正确的

if (ticket>0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"正在卖票" + ticket--);

}

}

}

//同步方法

public synchronized void method() {

synchronized (objA){//正确的  

if (ticket>0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"正在卖票" + ticket--);

}

}

}

}

 

将线程不安全的集合, 变为 线程安全的集合

  

  Collections: 集合工具类

  

  方法:

   public static <T> Collection<T> synchronizedCollection(Collection<T> c)

   public static <T> List<T> synchronizedList(List<T> list)

   public static <T> Set<T> synchronizedSet(Set<T> s)

   public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)

案例:

public class ThreadDemo {

public static void main(String[] args) {

//线程安全的集合

Vector v = new Vector();

Hashtable table = new Hashtable();

//线程安全的类

StringBuffer sb = new StringBuffer();

//创建一个线程不安全的集合

ArrayList list = new ArrayList();

//将线程不安全的集合, 变为 线程安全的集合

List synchronizedList = Collections.synchronizedList(list);

}

}

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock

void lock() 加锁

void unlock() 解锁

ReentrantLock

public class TicketThread {

public static void main(String[] args) {

//创建票对象

Ticket ticket = new Ticket();

//创建线程对象(窗口)

Thread t1 = new Thread(ticket, "窗口1");

Thread t2 = new Thread(ticket, "窗口2");

Thread t3 = new Thread(ticket, "窗口3");

//启动线程

t1.start();

t2.start();

t3.start();

}

}

public class Ticket implements Runnable {

 

//定义票

int ticket = 100;

//Object obj = new Object();

//定义锁对象

Lock ck = new ReentrantLock();

@Override

public void run() {

while (true) {

//synchronized (obj) {

ck.lock();//上锁

if (ticket > 0 ) {

System.out.println(Thread.currentThread().getName() + "正在卖票 :" + ticket--);

}

//}

ck.unlock();//解锁

}

}

 

}

 同步弊端:

效率低。

如果出现了同步嵌套,就容易产生死锁问题。

死锁问题及其代码

是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。

案例:

public class DieThread extends Thread {

public boolean flag;

public static Object objA = new Object();

public static Object objB = new Object();

//构造方法

public DieThread(String name, boolean b) {

super(name);// 调用Thread类中的构造方法,完成设置线程名称

flag = b;

}

 

@Override

public void run() {

while (true) {

if (flag) {

synchronized (objA) {

System.out.println(getName() + "if-- ObjA");

synchronized (objB) {

System.out.println(getName() + "if-- ObjB");

}

}

} else {

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (objB) {

System.out.println(getName() + "else-- ObjB");

synchronized (objA) {

System.out.println(getName() + "else-- ObjA");

}

}

}

}

}

 

}

 

  测试类

 

public class ThreadTest {

public static void main(String[] args) {

//创建线程对象

DieThread t1 = new DieThread("妹子", true);

DieThread t2 = new DieThread("班长", false);

t1.start();

t2.start();

}

}

线程通信案例:

  消费者(购买手机) : GetPhone

public class GetPhone implements Runnable {

Phone phone;

public GetPhone(Phone p) {

phone = p;

}

 

@Override

public void run() {

while (true) {

phone.get();

}

}

 

}

 

  手机类

public class Phone {

public String brand;

public String color;

public boolean isNewPhone = false;// 代表当前有新手机

//true 代表有新手机

//false 没有新手机

//生产手机

public synchronized void set(String brand, String color) {

//是否有新手机

if (this.isNewPhone) {

//说明有新手机

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//如果到了当前代码位置,说明没有新手机,要生产新手机

//生产手机

this.brand = brand;

this.color = color;

//模拟生产手机的耗时操作

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.print(Thread.currentThread().getName());//班长

System.out.println("生产了," +this.brand + "---" + this.color);

//手机生产完成,把手机状态 更新为 有新手机 true

this.isNewPhone = true;

//唤醒消费者,告诉她,有新手机了,可以购买

this.notify();

}

 

//购买手机

public synchronized void get() {

//判断是否有新手机

if (!this.isNewPhone) {

//说明没有新手机, 等待

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//执行到了当前代码,说明目前有手机了

//购买手机

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.print(Thread.currentThread().getName());//妹子

System.out.println("购买了," + this.brand +"---" +this.color);

//手机被买走了,更新手机的状态,  false

this.isNewPhone = false;

//唤醒生产商,通知生产商 生产新手机

this.notify();

}

}

 模拟生产商 与 消费者的案例  

  生产商(生产手机) : SetPhone

 消费者(购买手机) : GetPhone

 手机类: Phone

 测试类: PhoneTest

   

   保证 生产和购买的同一部手机

   

public class PhoneTest {

public static void main(String[] args) {

//手机对象

Phone p = new Phone();

//生产商

SetPhone set = new SetPhone(p);

//消费者

GetPhone get = new GetPhone(p);

//创建线程对象

Thread setThread = new Thread(set, "班长");

Thread getThread = new Thread(get, "妹子");

//启动线程

setThread.start();

getThread.start();

}

}

package cn.itcast_07_Phone;

/*

 * 生产商(生产手机) : SetPhone

 */

public class SetPhone implements Runnable {

 

Phone phone ; 

int num = 0;

public SetPhone(Phone p) {

phone = p;

}

 

@Override

public void run() {

while (true) {

if (num %2 == 0) {

phone.set("小米4","蓝色");

} else {

phone.set("苹果6s", "黄色");

}

num++;

}

}

}

 Object类中的方法

 

public final void wait() 

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

  public final void wait(long timeout)

         在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。

         该方法等价于  Thread类中sleep(long timeout)

   public final void wait(long timeout, int nanos)

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。 

  该方法等价于 Thread类中sleep(long timeout, int nanos)

 

 

   当前线程必须拥有此对象监视器(锁对象)

  该线程发布对此监视器的所有权(执行权)并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器(锁对象)上等待的线程醒来。

   然后该线程将等到重新获得对监视器(锁对象)的所有权(执行权)后才能继续执行。 

   

  public final void notify()

  唤醒在此对象监视器(锁对象)上等待的单个线程。

  如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的

  public final void notifyAll()

  唤醒在此对象监视器上等待的所有线程。

  线程通过调用其中一个 wait 方法,在对象的监视器上等待。 

  sleep() 与 wait()的区别,notify()的作用?

 

  sleep(): 让线程处于暂时休眠状态,当时间到达后,自动醒来运行线程对象

    注意: 在线程处理休眠状态的过程中, 不会释放锁对象,会释放CPU执行权

   

  wait():  让线程处于等待状态, 需要被其他线程对象,通过notify() 或者notifyAll() 进行唤醒,醒来后运行线程对象

  注意: 在线程处理等待状态的过程中, 会释放锁对象,会释放CPU执行权

  notify(): 唤醒处于等待状态的线程

  注意: 当把一个等待状态的线程唤醒后,不会立刻执行该线程,会先获取锁对象,获取到锁对象和CPU执行权后,才会执行线程

 

Executors: 用来创建线程池的工具类

  

  DK5新增了一个Executors工厂类来产生线程池,有如下几个方法

public static ExecutorService newCachedThreadPool()

创建一个具备高效缓冲效果的线程池对象

public static ExecutorService newFixedThreadPool(int nThreads)

创建一个可重用的线程池,参数用来指定当前线程池中有多少个线程对象

public static ExecutorService newSingleThreadExecutor()

创建一个包含一个线程对象的线程池对象

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。

它提供了如下方法

Future<?> submit(Runnable task)

<T> Future<T> submit(Callable<T> task)

 

public class ThreadPool {

public static void main(String[] args) {

//我想创建带有2个线程对象的线程池,返回的对象就是线程池对象

ExecutorService service = Executors.newFixedThreadPool(2);

//获取线程池中的某个线程,进行操作

service.submit(new MyRunnable());

service.submit(new MyRunnable());

service.submit(new MyRunnable());

}

}

public class MyRunnable implements Runnable {

 

@Override

public void run() {

System.out.println("我要找一个教练,教我游泳");

System.out.println(Thread.currentThread().getName());

}

}

 匿名内部类方式使用多线程

new Thread(){代码…}.start();

New Thread(new Runnable(){代码…}).start();

 

public class ThreadDemo {

public static void main(String[] args) {

//匿名内部类,Thread类的子类

new Thread(){

//Thread类的匿名子类对象

public void run() {

System.out.println("今天凉快");

};

}.start();

//--------------------------------

//匿名内部类方式,实现Runnable接口

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("呵呵,随便");

}

}).start();

//---------------------------------

for (int i = 0; i < 100; i++) {

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("呵呵,随便");

}

}).start();

}

}

}

 

/*

  定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能

 Timer

 public Timer()

 public void schedule(TimerTask task, long delay): 

  在指定的时间到达后,执行TimerTask中的run方法

 public void schedule(TimerTask task,long delay,long period)

  在指定的时间到达后,执行TimerTask中的run方法,并且以后每隔固定的时间重复执行

 

 TimerTask

 public abstract void run()

 public boolean cancel()

 开发中

  Quartz是一个完全由java编写的开源调度框架。

 

 */

public class TimerDemo {

public static void main(String[] args) {

/*

//3秒钟后 闹钟响起

Timer timer = new Timer();

//准备好定时器Timer一会要执行的内容(任务)

TimerTask task = new TimerTask() {

@Override

public void run() {//此计时器任务要执行的操作

System.out.println("啦啦啦,节日快乐");

}

};

timer.schedule(task, 3000);//3秒后执行task任务

*/

//-----------------------------------------

//5秒钟后 闹钟响起, 以后每隔3秒响一次

Timer timer = new Timer();

//准备好定时器Timer一会要执行的内容(任务)

TimerTask task = new TimerTask() {

@Override

public void run() {

System.out.println("啦啦啦,节日快乐");

}

};

timer.schedule(task, 5000, 3000);

}

}

 如何创建一个单例类呢?(饿汉式)

1:构造方法私有

2:在本类中,创建本类对象

3:对外提供公共访问方法,用来获取当前类对象

 

 

(懒汉式)

  1构造方法私有

2在本类中,创建本类对象的引用,而不创建对象

3对外提供公共访问方法,用来获取当前类对象 

如果是第一次访问方法: 完成对象的创建

如果不是第一次访问方法,返回创建好的对象

考虑线程安全问题, 加锁解决

注意了,在多线程的情况下,懒汉式的会有线程安全问题,饿汉式没有线程安全问题

懒汉式案例:

public class Single2 {

//构造方法私有

private Single2(){}

//在本类中,创建本类对象的引用,而不创建对象

private static Single2 s = null;

//对外提供公共访问方法,用来获取当前类对象 

//t1,t2,t3

public synchronized static Single2 getInstance(){

if (s == null) {

//t1, t2

//如果是第一次访问方法: 完成对象的创建

s = new Single2();

}

//如果不是第一次访问方法,返回创建好的对象

return s;

}

 

}

 (懒汉式)测试

 

public class Test2 {

public static void main(String[] args) {

for (int i = 0; i < 3; i++) {

new Thread(new Runnable() {

@Override

public void run() {

//获取对象

Single2 s = Single2.getInstance();

System.out.println(s);

}

}).start();

}

}

}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值