您的位置:首页 > 编程语言 > Java开发

Java学习笔记之多线程 2

2015-06-01 11:13 525 查看
多线程学习笔记二
一、线程间通信

线程间通信,其实就是多个线程在操作同一个资源,但是操作的动作不同。

1.1等待唤醒机制:

测试案例1:生产者生产一部手机,消费者就购买一部手机

//测试类

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();
}
}

//手机类:共享资源

public
class
Phone {
private Stringbrand;
private Stringcolor;
private
boolean
isNewPhone = false;//默认没有生产手机
//生产手机函数,同步函数的锁是this
public
synchronized void
set(String
brand, String color) {
//是否有新手机
if (this.isNewPhone) {
try {
this.wait();//锁对象调用Object类中的wait方法,让生产者处于等待状态。注意:锁对象可以是任意对象
} catch (InterruptedExceptione) {
e.printStackTrace();
}
}
//如果到了当前代码位置,说明没有新手机,要生产新手机
this.brand =brand;
this.color =color;
//模拟生产手机的耗时操作
try {
Thread.sleep(1000);
} catch(InterruptedExceptione) {
e.printStackTrace();
}
//打印生产后的信息
System.out.print(Thread.currentThread().getName());//生产者线程名称
System.out.println("生产了:" +this.brand
+ "---" + this.color);
//手机生产完成,把手机状态更新为有新手机 true
this.isNewPhone =true;
//唤醒消费者,有新手机了,可以购买
this.notify();
}
//购买手机函数,同步函数锁是this
public
synchronized void
get() {
//判断是否有新手机
if (!this.isNewPhone) {
try {
this.wait();//没有手机,消费者处于等待状态
} catch(InterruptedExceptione) {
e.printStackTrace();
}
}
//执行到了当前代码,说明目前有手机了
//购买手机
try {
Thread.sleep(1000);
} catch(InterruptedExceptione) {
e.printStackTrace();
}
//打印购买后的信息
System.out.print(Thread.currentThread().getName());//消费者
System.out.println("购买了:" +this.brand
+"---" +this.color);
//手机被买走了,更新手机的状态,false
this.isNewPhone =false;
//唤醒生产者,该生产新手机了
this.notify();
}
}

//生产者类

public
class
SetPhone implements Runnable {
Phone phone ;
int
num = 0;//线程体中的代码切换
//构造函数
public SetPhone(Phonep) {
phone =
p;
}
//重写run方法
public
void
run() {
while (true) {
if (num %2 == 0) {//线程体切换条件
phone.set("小米4","蓝色");//生产小米手机
} else {
phone.set("iPhone6","黄色");//生产苹果手机
}
num++;
}
}
}

//消费者类

public
class
GetPhone implements Runnable {
Phone phone;
//构造函数
public GetPhone(Phonep) {
phone =
p;
}
//重写run方法
public
void
run() {
while (true) {
phone.get();//消费手机
}
}
}

输出结果:

生产者生产了:小米4---蓝色
消费者购买了:小米4---蓝色
生产者生产了:iPhone6---黄色
消费者购买了:iPhone6---黄色

...

由输出结果可知:生产者生产了一部手机,就通知消费者来买手机,在等待消费者买手机的过程中,生产者停止生产,处于等待状态;消费者收到通知后,便来买手机,买完手机后也通知生产者没手机了,该生产手机了,于是消费者停止购买,处于等待状态,等待生产者生产手机...依此循环下去。程序执行过程如图1所示。



图1
注意:本例是在只有2个线程的理想情况下,实际中可能会有多个线程。

测试案例2:生产者与消费者升级版,JDK1.5中的新特性来处理,多个生产者,多个消费者的同步问题。

//测试类

classProducerConsumerDemo {

public static void main(String[] args) {

//创建共享资源对象

Resource r = new Resource();

//生产者对象

Producer pro = new Producer(r);

//消费者对象

Consumer con = new Consumer(r);

//创建线程对象

Thread t1 = new Thread(pro);//生产者1

Thread t2 = new Thread(pro);//生产者2

Thread t3 = new Thread(con);//消费者1

Thread t4 = new Thread(con);//消费者2

//开启线程

t1.start();

t2.start();

t3.start();

t4.start();

}

}

//JDK1.5中提供了多线程升级解决方案。

//共享资源

classResource {

private String name;

private int count = 1;

private boolean flag = false;

private Lock lock = new ReentrantLock();//创建lock对象

//生产者锁对象

private Condition condition_pro =lock.newCondition();

//消费者锁对象

private Condition condition_con =lock.newCondition();

//生产商品函数

public void set(String name)throws InterruptedException {

lock.lock();//上锁

try {

while(flag) {//有商品

condition_pro.await();//等待,不生产

}

// 执行到当前代码说明没有商品,开始生产商品

this.name =name+"--"+count++;

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

try {

Thread.sleep(500);

}catch(InterruptedException e){

e.printStackTrace();

}

//打印生产后的信息

System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);

//产品生产完成,把产品状态更新为有新产品:true

flag = true;

//唤醒消费者,有新产品,可以购买了

condition_con.signal();

}

Finally {

lock.unlock();//解锁,释放锁的动作一定要执行。

}

}

//消费商品函数

public void out()throws InterruptedException {

lock.lock(); //上锁

try {

while(!flag){//判断是否有新产品

condition_con.await();//没有新产品,消费者处于等待状态

}

//执行到了当前代码,说明目前有产品,可以购买了
//模拟购买产品的耗时动作

try {

Thread.sleep(500);

}catch(InterruptedException e){

e.printStackTrace();

}

//打印购买后的信息

System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);

//产品被买走了,更新产品的状态,false
flag = false;

//唤醒生产者,该生产新产品了
condition_pro.signal();

}

Finally {

lock.unlock();//解锁

}

}

}

//生产者类

classProducer implements Runnable {

private Resource res;

Producer(Resource res) {

this.res = res;

}

//重写run方法

public void run() {

while(true) {

try{

res.set("+商品+");

}

catch (InterruptedException e) {

//不处理

}

}

}

}

//消费者类

classConsumer implements Runnable {

private Resource res;

Consumer(Resource res) {

this.res = res;

}

public void run() {

while(true) {

try {

res.out();

} catch (InterruptedException e) {

//不处理异常

}

}

}

}

输出结果

Thread-0...生产者..+商品+--1

Thread-2...消费者.........+商品+--1

Thread-1...生产者..+商品+--2

Thread-3...消费者.........+商品+--2

Thread-0...生产者..+商品+--3

Thread-2...消费者.........+商品+--3

Thread-1...生产者..+商品+--4

Thread-3...消费者.........+商品+--4

...

二、Object类中和线程相关的方法

public final void wait()//在其他线程调用此对象的notify()方法或 notifyAll()方法前,导致当前线程等待
public final void wait(long timeout)//单位:ms
//在其他线程调用此对象的notify()方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。该方法等价于Thread类中sleep(long timeout)
public final void wait(long timeout,int nanos)
//在其他线程调用此对象的notify() 方法或 notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。该方法等价于 Thread类中sleep(longtimeout,int nanos)
public final void notify()//唤醒在此对象监视器(锁对象)上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的
publicfinal void notifyAll() //唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
sleep()与 wait()的区别,notify()的作用?
sleep():让线程处于暂时休眠状态,当时间到达后,自动醒来运行线程对象。注意:在线程处理休眠状态的过程中,不会释放锁对象,会释放CPU执行权
wait():让线程处于等待状态,需要被其他线程对象,通过notify()或者notifyAll()进行唤醒,醒来后运行线程对象。 注意:在线程处理等待状态的过程中,会释放锁对象,会释放CPU执行权。
notify():唤醒处于等待状态的线程。 注意:当把一个等待状态的线程唤醒后,不会立刻执行该线程,会先获取锁对象,获取到锁对象和CPU执行权后,才会执行线程。
三、线程其他操作

1、停止线程

停止线程其实只有一种方法,就是让run方法结束。让run方法结束有一下3种方式:

1.1、定义循环结束标记,因为线程运行代码一般都是循环,只要控制了循环即可。注意:特殊情况,当线程处于了冻结状态,就不会读取到标记。那么线程就不会结束。

1.2、使用interrupt(中断)方法,该方法让处于冻结状态的线程,强制回到运行状态中来。

1.3、Thread类中的stop方法,此方法已经过时不再使用。

测试案例3

class StopThread implements Runnable {

private booleanflag = true;

publicsynchronized void run() {

while(flag) {

try{

wait();//当线程处于了冻结状态,就不会读取到标志位。那么线程就不会结束

}catch(InterruptedExceptione){

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

flag =false;//线程被强制醒来后,可以操作标记让线程结束

}

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

}

}

public voidchangeFlag() {

flag = false;

}

}

//测试类

class StopThreadDemo {

public staticvoid main(String[] args) {

//创建自定义类对象

StopThread st= new StopThread();

//创建自定义类对象

Thread t1 =new Thread(st);

Thread t2 =new Thread(st);

//创建线程对象

t1.setDaemon(true);

t2.setDaemon(true);

//开启线程

t1.start();

t2.start();

int num = 0;

while(true) {

if(num++== 60) {

t1.interrupt();//把处于冻结状态的线程强制恢复到运行态

t2.interrupt();

break; //退出死循环

}

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

}

System.out.println("mainover");//主线程执行完毕标志

}

}

输出结果:

main.......58

main.......59

main.......60

main over

Thread-0....Exception

Thread-0....run

Thread-1....Exception

Thread-1....run

2、守护线程

简介:相当于后台线程,任然会和前台线程(例如主线程)抢夺CPU资源(具备执行权),但是,当所有的前台线程都结束后,后台线程会自动结束。后台线程和前台线程有一种依赖关系。

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

public final boolean isDaemon() //测试该线程是否为守护线程

测试案例4

//1自定义类实现runnable接口

classStopThread implements Runnable {

privateboolean flag =true;

//2重写run方法

public voidrun() {

while(flag){

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

}

}

public voidchangeFlag() {

flag =false;

}

}

//测试类

class StopThreadDemo {

publicstatic void main(String[] args) {

//3创建自定义类对象

StopThreadst = new StopThread();

//4创建线程对象

Threadt1 = new Thread(st);

Threadt2 = new Thread(st);

//5设置为守护线程,必须在启动线程前完成

t1.setDaemon(true);

t2.setDaemon(true);

//如果自定义线程都是守护线程,那么程序执行完主线程后,JVM虚拟机就直接退出,不再执行自定义线程了。

//6启动线程

t1.start();

t2.start();

int num= 0;

while(true){

if(num++== 60) {

break;//退出死循环

}

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

}

System.out.println("mainover");//主线程执行完毕标志

}

}

输出结果:

main.......59

Thread-1....run

main.......60

Thread-0....run

main over //主线程结束

Thread-1....run

Thread-0....run

请按任意键继续. . .

由输出结果可知:当主线程结束时,守护线程也随之结束。

3、Join方法

简介:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。

public final void join()

测试案例5

//自定义类

class Demo implements Runnable{

//重写run方法

public void run() {

for(int x=0; x<70; x++) {

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

}

}

}

//测试类

class JoinDemo {

public static void main(String[] args) throws Exception {

Demo d = new Demo();

Thread t1 = new Thread(d);

Thread t2 = new Thread(d);

t1.start();

t1.join();//会抛出异常,t1抢到主线程的CPU执行权开始执行(此时主线程处于冻结状态),等线程t1执行完毕后其他线程才能继续执行。也就是说主线程要等到t1挂掉后他才能运行。

t2.start();

//t1.join();//当t1在此处加入线程时,主线程处于冻结状态,线程t1,t2处于就绪态,那么t1,t2两个线程在CPU的切换下轮流执行。当t1执行完毕,不管t2是否执行完毕,主线程已经“活过来了”,此时主线程和t2处于抢夺cpu的状态,这两个线程在CPU的切换下执行直到所有线程都结束为止。

//主线程

for(int x=0; x<80; x++) {

System.out.println("main....."+x);

}

System.out.println("over");

}

}

输出结果:略

4、Priority和yield方法

优先级表示抢夺CPU的频率。优先级从1-10,1级最低,10级最高,所有的线程的默认优先级都是5。

publicfinal void setPriority(int newPriority)//更改线程的优先级

publicstatic void yield()//暂停当前正在执行的线程对象,并执行其他线程。Yield:放弃

测试案例6

//自定义类实现runnable接口

class Demo implements Runnable {

public void run() {

for(int x=0; x<70; x++) {

System.out.println(Thread.currentThread().toString()+"....."+x);

//Thread.yield();//临时释放CPU执行权,减缓线程的运行,让线程都有机会平均运行的效果。

}

}

}

//测试类

class JoinDemo {

public static void main(String[] args) throws Exception {

Demo d = new Demo();

Thread t1 = new Thread(d);

Thread t2 = new Thread(d);

t1.start();

//当数据是固定时就定义成常量,当数据是共享时就定义成static

t1.setPriority(Thread.MAX_PRIORITY);//把t1设置为最高优先级

t2.start();

//主线程

for(int x=0; x<80; x++) {

//不操作

}

System.out.println("over");

}

}

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

方式1:

new Thread(){重写run方法...}.start();
方式2:
New Thread(new Runnable(){重写run方法...}).start();
测试案例7:
public
class
ThreadDemo {
public
static void
main(String[]
args) {
//匿名内部类,Thread类的子类
new Thread(){
//Thread类的匿名子类对象
public
void
run() {
System.out.println("Thread匿名内部类对象");
};
}.start();
//匿名内部类方式,实现Runnable接口,父类引用指向子类对象
new Thread(new Runnable() {
@Override
public
void
run() {
System.out.println("Runnable接口匿名内部类子类对象");
}
}).start();
}
}
五、线程组

5.1线程组简介:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。默认情况下,所有的线程都属于主线程组。从JDK1.0开始。

5.2 ThreadGroup构造方法

publicThreadGroup(Stringname)//构造一个新线程组。新线程组的父线程组是目前正在运行线程的线程组

publicThreadGroup(ThreadGroup parent,Stringname)//创建一个新线程组。新线程组的父线程组是指定的线程组

5.3 ThreadGroup常用方法

publicfinal
StringgetName()//返回此线程组的名称

5.3Thread类有关线程组的构造方法

public Thread(ThreadGroupgroup, String name)

public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group,Runnable target, String name)

//给线程设置分组。

5.4 Thread类有关线程组的常用方法

publicfinal ThreadGroup getThreadGroup()//返回该线程所属的线程组

5.5测试案例:

public
class
ThreadDemo {
public
static void
main(String[]
args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
//创建一个线程组
MyThreadGroupgroup =
new
MyThreadGroup("javaEE");
//创建一个新的线程对象,并把该线程对象设置为javaEE组的成员
MyThread t3 = new MyThread(group,"se");
t3.start();
}
}

//线程类

public
class
MyThread extends Thread {
public MyThread() {
super();
}
publicMyThread(ThreadGroupgroup, String
name) {
super(group,name);
}
public
void
run() {
System.out.println("当前线程的名字是:" + getName());
//获取到线程组对象
ThreadGroup threadGroup =this.getThreadGroup();
//获取线程组的名字
String name =
threadGroup.getName();
System.out.println("当前线程对象所在的组名是: " +name);
}
}

//线程组类

public
class
MyThreadGroup extends ThreadGroup {
publicMyThreadGroup(Stringname) {
super(name);
}
}

六、线程池

6.1简介:程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

在JDK5之前,必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。

JDK5新增了一个Executors(执行者)工厂类来产生线程池。

6.2 Executors类的构造方法:无

6.3 Executors类的常用方法:

publicstatic ExecutorService newCachedThreadPool()//创建一个具有高效缓存功能的线程池对象

publicstatic ExecutorService newFixedThreadPool(int nThreads)//创建一个可重用的线程池,具有固定(Fixed)线程数的线程池,n为1时也可以创建单个线程对象的线程池

publicstatic ExecutorService newSingleThreadExecutor()//创建单个线程对象的线程池对象。

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

ExecutorService接口常用方法:(JDK1.5)

注意:ExecutorService直译为“执行者服务”在java中叫线程池。

Future<?>submit(Runnable task)//提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。(submit:提交)

<T>Future<T> submit(Callable<T> task)// 提交一个 Runnable任务用于执行,并返回一个表示该任务的 Future。

Future<V>接口简介(jdk1.5)

Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。

6.4 测试案例

//1、自定义类,实现runnable接口
public
class
MyRunnable implements Runnable {
//2、重写run方法,线程体
public
void
run() {
System.out.println("我是线程体");
//获取线程名字
System.out.println(Thread.currentThread().getName());
}
}

//测试类

public
class
ThreadPool {
public
static void
main(String[]
args) {
//3、用Executors类中的newFixedThreadPool()方法创建带有2个线程对象的线程池对象(用方法的返回值"创建"对象)。
ExecutorServiceservice = Executors.newFixedThreadPool(2);
//4、将线程体对象当做参数传递给ExecutorService接口中的submit()方法,来启动线程,执行现场体。
//submit()方法相当于Thread类中的start()方法.
service.submit(new MyRunnable());
service.submit(new MyRunnable());
}
}

输出结果:

我是线程体
我是线程体
pool-1-thread-2
pool-1-thread-1

七、java多线程实现方式3,实现Callable接口

1 简介

通过线程池对象,实现Callable(随时可偿还的)接口。Callable接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。从JDK1.5开始。

2构造方法:无

3 普通方法

V call()//计算结果,如果无法计算结果,则抛出一个异常.相当于runnable接口中的run方法。

4 创建线程的步骤

1) 自定义类实现Runnable接口

2) 重写call方法

3) 创建线程池对象

4) 实现线程池中的线程对象,来执行线程要执行的代码

5测试案例

//1、自定义类实现Callable接口
public
class
MyCallable implementsCallable{
//2、重写call方法,相当于Runnable接口中的run()方法
public Object call()throws Exception {
System.out.println("创建线程的第三种方法,通过线程池对象实现Callable接口");
System.out.println(Thread.currentThread().getName());
return
null
;
}
}

//测试类

public
class
ThreadTest {
public
static void
main(String[]
args) {
//3、创建线程池对象
ExecutorServiceservice = Executors.newFixedThreadPool(2);
//4、实现线程池中的线程对象,来执行线程要执行的代码
service.submit(new MyCallable());
service.submit(new MyCallable());
}
}
输出结果:



由输出结果可知:程序一直处于等待状态。

八、定时器:

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

2 Timer类

一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。位于java.util包中。从JDK1.3开始。

Timer类构造方法:

public Timer()//创建一个新计时器
常用方法:
publicvoid schedule(TimerTask task, long delay) //在指定的时间到达后,执行TimerTask中的run方法
public void schedule(TimerTask task,longdelay,long period)//在指定的时间到达后,执行TimerTask中的run方法,并且以后每隔固定的时间重复执行
3 TimerTask类
是一个抽象类。由Timer 安排为一次执行或重复执行的任务,位于java.util包中,从JDK1.3开始。
TimerTask构造方法
protected TimerTask()//创建一个新的计时器任务
TimerTask常用方法
public abstract void run()//此计时器任务要执行的操作。
public boolean cancel()//取消此计时器任务。
备注:在android开发中Quartz是一个完全由java编写的开源调度框架。
4 测试案例:设置一个定时器,5秒钟后闹钟响起,以后每隔3秒响一次。
public
class
TimerDemo {
public
static void
main(String[]
args) {
//1、创建timer对象
Timer timer = new Timer();
//2、匿名方式创建TimerTask对象
TimerTask task =
new
TimerTask() {
//3、重写run()方法,准备好定时器Timer要执行的内容(任务)
public
void
run() {
System.out.println("除非你能在床上挣钱,否则就别赖在床上不起");
}
};
//4、执行任务
timer.schedule(task, 5000, 3000);
}
}
九、设计模式

1概述

设计模式(Designpattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让带码更容易被他人理解、保证代码可靠性。

设计模式不是一种方法和技巧,而是一种思想。

设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,实现低耦合,高内聚。

2设计模式分类

创建型模式简单工程模式工厂方法模式抽象工厂模式,建造者模式,原型模式单例模式(6个)

行为型模式模板方法模式观察者模式,状态模式,职责链模式,命令模式,访问者模式,策略模式,备忘录模式,迭代器模式,解释器模式(10个)

结构型模式:外观模式,适配器模式代理模式装饰模式,桥接模式,组合模式,享元模式(7个)

备注:红色为实际编程中常用的设计模式。

3单例设计模式

概述:在内存中一个类只能有一个实例对象。

分类:

饿汉式:创建步骤 1、构造方法私有,2、在本类中创建本类对象,3、对外提供公共访问方法,用来获取当前类对象。

懒汉式:创建步骤1、构造方法私有,2、在本类中创建本类的对象引用,而不是创建对象,3、对外提供公共访问方法,用来获取当前类对象,3.1如果是第一次访问方法,完成对象的创建,3.2如果不是第一次访问对象,返回创建好的对象。

测试案例1:创建饿汉式单例设计模式

//饿汉式

public
class
Single {
//1、构造方法私有
private Single(){}
//2、在本类中,创建本类对象
private
static
Single s =
new
Single();
//3、对外提供公共访问方法,用来获取当前类对象
public
static
SinglegetInstance(){
returns;
}
}

//测试类

public
class
Test {
public
static void
main(String[]
args) {
//
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 ==s2);//判断对象引用s1、s2指向的地址是否相等
System.out.println(s1);
System.out.println(s2);
}
}

输出结果:

true
Single@659e0bfd
Single@659e0bfd
由输出结果可知,s1、s2指向堆中同一块内存地址。

测试案例2:创建懒汉式单例设计模式

//懒汉式

public
class
Single2 {
//构造方法私有
private Single2(){}
//在本类中,创建本类对象的引用,而不创建对象
private
static
Single2 s =
null
;
//对外提供公共访问方法,用来获取当前类对象
public
synchronized static
Single2getInstance(){
if (s ==null) {
//t1, t2
//如果是第一次访问方法:完成对象的创建
s =
new
Single2();
}
//如果不是第一次访问方法,返回创建好的对象
returns;
}
}

//测试类

public
class
Test2 {
public
static void
main(String[]
args) {
Single2 s1 = Single2.getInstance();
Single2 s2 = Single2.getInstance();
System.out.println(s1 ==s2);
System.out.println(s1);
System.out.println(s2);

}
}

输出结果

true
Single2@659e0bfd
Single2@659e0bfd
由输出结果可知,s1、s2指向堆中同一块内存地址。

注意:在多线程的情况下,懒汉式的会有线程安全问题,饿汉式没有线程安全问题。开发的时候用饿汉式。
十、多线程常见问题

1多线程有几种实现方案,分别是哪几种?

答:3种,

方式一:继承Thread类,重写run方法

方式二:实现Runnable接口,实现run方法

方式三:通过线程池,实现Callable接口,实现call()方法。

2同步有几种方式,分别是什么?

答:3种

方式一:同步代码块,锁对象是任意的

方式二:同步函数,普通同步函数的锁对象是this,静态同步函数的锁是类名.class

方式三:Lock锁

当一个程序有多个线程操作同一个共享数据时,要求线程对象所使用的是同一把锁对象。

3 启动一个线程是run()还是start()?它们的区别?

答:启动一个线程是start()方法,

run()方法是线程要执行的操作,线程体

start()方法启动线程,告诉JVM调用run()方法。

4 sleep()和wait()方法的区别?

答:sleep()方法是让线程休眠指定的时间,时间到后自动醒来。在线程休眠的过程中不会释放锁对象,但会释放CPU执行权。

wait()方法是让线程处于等待状态,当设置指定的时间,时间到后会自动醒来,如果没有设置等待时间,则必须由notify()或notifyAll()方法来唤醒。在线程等待的过程中会释放CPU的执行权和锁对象。

5 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

答:因为这些方法的调用,要使用当前线程中的锁对象来调用,而锁对象的类型是任意对象,所以,放在Object类中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: