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

24 API-多线程(多线程(多线程JDK5Lock锁,生产者消费者等待唤醒机制,定时器),设计模式(设计原则,设计模式(简单工程,工厂方法,单例模式)

2016-03-29 22:22 746 查看

1:多线程(理解)

(1)JDK5以后的针对线程的锁定操作和释放操作

Lock锁

* 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,

* 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。

*

* Lock:

* void lock(): 获取锁。

* void unlock():释放锁。

* ReentrantLock是Lock的实现类.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {

// 定义票
private int tickets = 100;

// 定义锁对象
private Lock lock = new ReentrantLock();

@Override
public void run() {
while (true) {
try {
// 加锁
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
}
} finally {
// 释放锁
lock.unlock();
}
}
}

}


(2)死锁问题的描述和代码体现

public class DieLock extends Thread {

private boolean flag;

public DieLock(boolean flag) {
this.flag = flag;
}

@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if objA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
/*
* 同步的弊端:
* 		A:效率低
* 		B:容易产生死锁
*
* 死锁:
* 		两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。
*
* 举例:
* 		中国人,美国人吃饭案例。
* 		正常情况:
* 			中国人:筷子两支
* 			美国人:刀和叉
* 		现在:
* 			中国人:筷子1支,刀一把
* 			美国人:筷子1支,叉一把
*/
public class DieLockDemo {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);

dl1.start();
dl2.start();
}
}
public class MyLock {
// 创建两把锁对象
public static final Object objA = new Object();
public static final Object objB = new Object();
}


(3)生产者和消费者多线程体现(线程间通信问题)



以学生作为资源来实现的



资源类:Student

设置数据类:SetThread(生产者)

获取数据类:GetThread(消费者)

测试类:StudentDemo

代码:

A:最基本的版本,只有一个数据。

B:改进版本,给出了不同的数据,并加入了同步机制

C:等待唤醒机制改进该程序,让数据能够实现依次的出现

wait() 等待

notify() 唤醒单个线程

notifyAll() (多生产多消费) 唤醒所有线程

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Student
{
// String name;
// int age;
// boolean flag;
private String name;
private int age;
private boolean flag;

public synchronized void set(String name, int age)
{
if (this.flag)
{
try
{
this.wait();
} catch (InterruptedException e)
{

e.printStackTrace();
}
}
this.name = name;
this.age = age;
this.flag = true;
this.notify();
}

public synchronized void get()
{
if (!this.flag)
{
try
{
this.wait();
} catch (InterruptedException e)
{

e.printStackTrace();
}
}
System.out.println(this.name + "===" + this.age);
this.flag = false;
this.notify();
}

}

class Producer implements Runnable
{
Student s;

Producer(Student s)
{
this.s = s;
}

int x = 0;

@Override
public void run()
{

while (true)
{

// synchronized (s) {
// if (s.flag) {
// try {
// s.wait();
// } catch (InterruptedException e) {
//
// e.printStackTrace();
// }
// }
if (x % 2 == 0)
{
// s.name = "张三";
// s.age = 21;
s.set("张三", 21);
x++;
} else
{
// s.name = "渣渣";
// s.age = 1;
s.set("渣渣", 1);
x++;
}
// s.flag = true;
// s.notify();
// }
//
}

}

}

class Consumer implements Runnable
{
Student s;

Consumer(Student s)
{
this.s = s;
}

@Override
public void run()
{

while (true)
{
// synchronized (s) {
// if (!s.flag) {
// try {
// s.wait();
// } catch (InterruptedException e) {
//
// e.printStackTrace();
// }
// }
// System.out.println(s.name + "==" + s.age);
//
// s.flag = false;
// s.notify();
// }
s.get();
}

}

}

public class TestWaitNotify
{

public static void main(String[] args)
{
//		ThreadGroup tg= new ThreadGroup("线程组");
Student s = new Student();
//		Producer pro = new Producer(s);
//		Consumer con = new Consumer(s);
//		Thread t1 = new Thread(tg,pro,"线程1");
//		Thread t2 = new Thread(tg,con,"线程2");
//		t1.getThreadGroup().getName();
//
//
//		t1.start();
//		t2.start();
//线程池
ExecutorService pool=Executors.newFixedThreadPool(2);
pool.submit(new Producer(s));
pool.submit(new Consumer(s));
//结束线程池
pool.shutdown();

}

}


为什么这些方法不定义在Thread类中呢?

* 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。

* 所以,这些方法必须定义在Object类中。

D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中

(4)线程组

* 线程组: 把多个线程组合到一起。

* 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

// 我们如何修改线程所在的组呢?

// 创建一个线程组

// 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组

(5)线程池

public class MyRunnable implements Runnable {

@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}

}


import java.util.concurrent.Callable;

//Callable:是带泛型的接口。
//这里指定的泛型其实是call()方法的返回值类型。
public class MyCallable implements Callable {

@Override
public Object call() throws Exception {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
return null;
}

}


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
* 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
*
* 如何实现线程的代码呢(多线程实现第三种方法)?
* 		A:创建一个线程池对象,控制要创建几个线程对象。
* 			public static ExecutorService newFixedThreadPool(int nThreads)
* 		B:这种线程池的线程可以执行:
* 			可以执行Runnable对象或者Callable对象代表的线程
* 			做一个类实现Runnable接口。
* 		C:调用如下方法即可
* 			Future<?> submit(Runnable task)
*			<T> Future<T> submit(Callable<T> task)
*		D:我就要结束,可以吗?
*			可以。
*/
public class ExecutorsDemo {
public static void main(String[] args) {
// 创建一个线程池对象,控制要创建几个线程对象。
// public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService pool = Executors.newFixedThreadPool(2);

// 可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());

//结束线程池
pool.shutdown();
}
}


(6)多线程实现的第三种方案

匿名内部类方式开线程

/*
* 匿名内部类的格式:
* 		new 类名或者接口名() {
* 			重写方法;
* 		};
* 		本质:是该类或者接口的子类对象。
*/
public class ThreadDemo {
public static void main(String[] args) {
// 继承Thread类来实现多线程
new Thread() {
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":"
+ x);
}
}
}.start();

// 实现Runnable接口来实现多线程
new Thread(new Runnable() {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":"
+ x);
}
}
}) {
}.start();

// 更有难度的
new Thread(new Runnable() {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println("hello" + ":" + x);
}
}
}) {
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println("world" + ":" + x);
}
}
}.start();
}
}


定时器

import java.util.Timer;
import java.util.TimerTask;

/*
* 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
* 依赖Timer和TimerTask这两个类:
* Timer:定时
* 		public Timer()
* 		public void schedule(TimerTask task,long delay)
* 		public void schedule(TimerTask task,long delay,long period)
* 		public void cancel()
* TimerTask:任务
*/
public class TimerDemo {
public static void main(String[] args) {
// 创建定时器对象
Timer t = new Timer();
// 3秒后执行爆炸任务
// t.schedule(new MyTask(), 3000);
//结束任务
t.schedule(new MyTask(t), 3000);
}
}

// 做一个任务
class MyTask extends TimerTask {

private Timer t;

public MyTask(){}

public MyTask(Timer t){
this.t = t;
}

@Override
public void run() {
System.out.println("beng,爆炸了");
t.cancel();
}

}


(7)多线程的面试题

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

两种。

继承Thread类

实现Runnable接口

扩展一种:实现Callable接口。这个得和线程池结合。

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

两种。

同步代码块

同步方法

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

start();

run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用

start():启动线程,并由JVM自动调用run()方法

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

sleep():必须指时间;不释放锁。

wait():可以不指定时间,也可以指定时间;释放锁。

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

因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。

而Object代码任意的对象,所以,定义在这里面。

6:线程的生命周期图

新建 -- 就绪 -- 运行 -- 死亡

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

建议:画图解释。

2:设计模式(理解)

(1)面试对象的常见设计原则

单一职责原则

•其实就是开发人员经常说的”高内聚,低耦合”
•也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则。

开闭原则

•核心思想是:一个对象对扩展开放,对修改关闭。
•其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。
•也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的

里氏替换原则

•核心思想:在任何父类出现的地方都可以用它的子类来替代。
•其实就是说:同一个继承体系中的对象应该有共同的行为特征。

依赖注入原则

•核心思想:要依赖于抽象,不要依赖于具体实现。
•其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。

接口分离原则

•核心思想:不应该强迫程序依赖它们不需要使用的方法。
•其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中。

迪米特

•核心思想:一个对象应当对其他对象尽可能少的了解
•其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用

(2)设计模式概述和分类

A:经验的总结

B:三类

创建型

结构型

行为型

(3)改进的设计模式

A:简单工厂模式

l简单工厂模式概述
•又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
l优点
•客户端不需要在负责对象的创建,从而明确了各个类的职责
l缺点
•这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护

B:工厂方法模式

l工厂方法模式概述
•工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
l优点
•客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
l缺点
•需要额外的编写代码,增加了工作量

C:单例模式(掌握)

l单例设计模式概述
•单例模式就是要确保类在内存中只有一个对象,该实例必须自动创建,并且对外提供。
l优点
•在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
l缺点
•没有抽象层,因此扩展很难。
•职责过重,在一定程序上违背了单一职责

* 单例模式:保证类在内存中只有一个对象。

*

* 如何保证类在内存中只有一个对象呢?

* A:把构造方法私有

* B:在成员位置自己创建一个对象

* C:通过一个公共的方法提供访问

*/

a:饿汉式

public class Student {
// 构造私有
private Student() {
}

// 自己造一个
// 静态方法只能访问静态成员变量,加静态
// 为了不让外界直接访问修改这个值,加private
private static Student s = new Student();

// 提供公共的访问方式
// 为了保证外界能够直接使用该方法,加静态
public static Student getStudent() {
return s;
}
}


b:懒汉式

/*
* 单例模式:
* 		饿汉式:类一加载就创建对象
* 		懒汉式:用的时候,才去创建对象
*
* 面试题:单例模式的思想是什么?请写一个代码体现。
*
* 		开发:饿汉式(是不会出问题的单例模式)
* 		面试:懒汉式(可能会出问题的单例模式)
* 			A:懒加载(延迟加载)
* 			B:线程安全问题
* 				a:是否多线程环境	是
* 				b:是否有共享数据	是
* 				c:是否有多条语句操作共享数据 	是
*/
public class Teacher {
private Teacher() {
}

private static Teacher t = null;

public synchronized static Teacher getTeacher() {
// t1,t2,t3
if (t == null) {
//t1,t2,t3
t = new Teacher();
}
return t;
}
}


(4)Runtime

* Runtime:每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。

* exec(String command)

JDK提供的一个单例模式应用的类。

还可以调用dos命令。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: