传智播客风清扬视频-------线程简介2

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

Lock:
void lock() :获取锁
void unlock() :释放锁
ReentrantLock是Lock的实现类



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

}

public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();

// 创建三个窗口
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

// 启动线程
t1.start();
t2.start();
t3.start();
}
}




同步的弊端: A:效率低 B:容易产生死锁

死锁: 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待现象。


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


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


public class DieLockDemo {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);

dl1.start();
dl2.start();
}
}




线程间通信: 生产者和消费者模式

资源类 :Student
设置学生数据: SetThread(生产者)
获取学生数据: GetThread(消费者)
测试类: StudentDemo

版本一:

public class Student {
String name;
int age;
}

public class GetThread implements Runnable {
private Student s;

public GetThread(Student s) {
this.s = s;
}

@Override
public void run() {
// Student s = new Student();
System.out.println(s.name + "---" + s.age);
}

}

public class SetThread implements Runnable {

private Student s;

public SetThread(Student s) {
this.s = s;
}

@Override
public void run() {
// Student s = new Student();
s.name = "林青霞";
s.age = 27;
}

}


public class StudentDemo {
public static void main(String[] args) {
//创建资源
Student s = new Student();

//设置和获取的类
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);

//线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);

//启动线程
t1.start();
t2.start();
}
}




修改版本一:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
A: 同一个数据出现多次
B:姓名和年龄不匹配
原因: A:同一个数据出现多欠: CPU的一点点时间片的执行权,就足够它执行多次
B:姓名和年龄不匹配: 线程运行的随机性

线程安全问题:
A: 是否是多线程环境 B: 是否共享数据 C: 是否有多条语句操作共享数据
解决方案:
加锁;
注意: A:不同种类的线程都要加锁。 B:不同种类的线程加的锁必须是同一把

加锁后的版本:

public class GetThread implements Runnable {
private Student s;

public GetThread(Student s) {
this.s = s;
}

@Override
public void run() {
while (true) {
synchronized (s) {
System.out.println(s.name + "---" + s.age);
}
}
}
}


public class SetThread implements Runnable {

private Student s;
private int x = 0;

public SetThread(Student s) {
this.s = s;
}

@Override
public void run() {
while (true) {
synchronized (s) {
if (x % 2 == 0) {
s.name = "林青霞";//刚走到这里,就被别人抢到了执行权
s.age = 27;
} else {
s.name = "刘意"; //刚走到这里,就被别人抢到了执行权
s.age = 30;
}
x++;
}
}
}
}


public class StudentDemo {
public static void main(String[] args) {
//创建资源
Student s = new Student();

//设置和获取的类
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);

//线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);

//启动线程
t1.start();
t2.start();
}
}



问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
如何实现呢?
通过Java提供的等待唤醒机制解决。


等待唤醒:
Object类中提供了三个方法:
wait() : 等待
notify() : 唤醒单个线程
notifyAll() : 唤醒所有线程

为什么不定义在Thread类中?
这些方法的调用必须通过锁对象调用,而代码块中的锁对象是任意对象,
所以这些方法必须定义在Object类中。

加了等待唤醒后的版本:

public class Student {
String name;
int age;
boolean flag; // 默认情况是没有数据,如果是true,说明有数据
}

public class GetThread implements Runnable {
private Student s;

public GetThread(Student s) {
this.s = s;
}

@Override
public void run() {
while (true) {
synchronized (s) {
if(!s.flag){
try {
s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
} catch (InterruptedException e) {
e.printStackTrace();
}
}

System.out.println(s.name + "---" + s.age);
//林青霞---27
//刘意---30

//修改标记
s.flag = false;
//唤醒线程
s.notify(); //唤醒t1
}
}
}
}


public class SetThread implements Runnable {

private Student s;
private int x = 0;

public SetThread(Student s) {
this.s = s;
}

@Override
public void run() {
while (true) {
synchronized (s) {
//判断有没有
if(s.flag){
try {
s.wait(); //t1等着,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}

if (x % 2 == 0) {
s.name = "林青霞";
s.age = 27;
} else {
s.name = "刘意";
s.age = 30;
}
x++; //x=1

//修改标记
s.flag = true;
//唤醒线程
s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
}
//t1有,或者t2有
}
}
}

public class StudentDemo {
public static void main(String[] args) {
//创建资源
Student s = new Student();

//设置和获取的类
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);

//线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);

//启动线程
t1.start();
t2.start();
}
}





线程组 ThreadGroup
线程组: 把多个 线程组合到一起。
它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。


public class MyRunnable implements Runnable {

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

}


public class ThreadGroupDemo {
public static void main(String[] args) {
// method1();

// 我们如何修改线程所在的组呢?
// 创建一个线程组
// 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组
method2();

// t1.start();
// t2.start();
}

private static void method2() {
// ThreadGroup(String name)
ThreadGroup tg = new ThreadGroup("这是一个新的组");

MyRunnable my = new MyRunnable();
// Thread(ThreadGroup group, Runnable target, String name)
Thread t1 = new Thread(tg, my, "林青霞");
Thread t2 = new Thread(tg, my, "刘意");

System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());

//通过组名称设置后台线程,表示该组的线程都是后台线程
tg.setDaemon(true);
}

private static void method1() {
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my, "林青霞");
Thread t2 = new Thread(my, "刘意");
// 我不知道他们属于那个线程组,我想知道,怎么办
// 线程类里面的方法:public final ThreadGroup getThreadGroup()
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
// 线程组里面的方法:public final String getName()
String name1 = tg1.getName();
String name2 = tg2.getName();
System.out.println(name1);
System.out.println(name2);
// 通过结果我们知道了:线程默认情况下属于main线程组
// 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组
System.out.println(Thread.currentThread().getThreadGroup().getName());
}
}



生产者和消费者最终版本:
把Student的成员变量私有
把设置和获取的操作封装成功能,并添加同步
设置或获取的线程里只需要调用方法即可



public class Student {
private String name;
private int age;
private boolean flag; // 默认情况是没有数据,如果是true,说明有数据

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


public class GetThread implements Runnable {
private Student s;

public GetThread(Student s) {
this.s = s;
}

@Override
public void run() {
while (true) {
s.get();
}
}
}


public class SetThread implements Runnable {

private Student s;
private int x = 0;

public SetThread(Student s) {
this.s = s;
}

@Override
public void run() {
while (true) {
if (x % 2 == 0) {
s.set("林青霞", 27);
} else {
s.set("刘意", 30);
}
x++;
}
}
}

public class StudentDemo {
public static void main(String[] args) {
//创建资源
Student s = new Student();

//设置和获取的类
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);

//线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);

//启动线程
t1.start();
t2.start();
}
}





线程池:

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

如何实现线程池的代码?
A: 创建一个线程池对象,控制要创建几个线程对象。
public static ExecutorService newFixedThreadPool(int nThreds)
B: 这种线程池的线程可以执行:
可以执行Runnable对象或Callable对象代表的线程
做一个类实现Runnable接口
C: 调用如下方法即可
Future<?> submit(Runnable task)
T Future<T> submit(Callable task)



public class MyRunnable implements Runnable {

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

}


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



使用Callable实现


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

}


public class CallableDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);

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

//结束
pool.shutdown();
}
}




通过Callable可以返回泛型对象,实现求和


import java.util.concurrent.Callable;

/*
* 线程求和案例
*/
public class MyCallable implements Callable<Integer> {

private int number;

public MyCallable(int number) {
this.number = number;
}

@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}

}


public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);

// 可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));

// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();

System.out.println(i1);
System.out.println(i2);

// 结束
pool.shutdown();
}
}



使用匿名内部类实现线程
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();

// 更有难度的,打印的是Thread类里重写的run()方法
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();
}
}




定时器
可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
依赖Timer和TimerTask这两个类:
Timer:定时
public Timer()
public void schedule(TimerTask task)
public void schedule(TimerTask task)
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();
}

}





public class TimerDemo2 {
public static void main(String[] args) {
// 创建定时器对象
Timer t = new Timer();
// 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸
t.schedule(new MyTask2(), 3000, 2000);
}
}

// 做一个任务
class MyTask2 extends TimerTask {
@Override
public void run() {
System.out.println("beng,爆炸了");
}
}






/*
* 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo)
*/

class DeleteFolder extends TimerTask {

@Override
public void run() {
File srcFolder = new File("demo");
deleteFolder(srcFolder);
}

// 递归删除目录
public void deleteFolder(File srcFolder) {
File[] fileArray = srcFolder.listFiles();
if (fileArray != null) {
for (File file : fileArray) {
if (file.isDirectory()) {
deleteFolder(file);
} else {
System.out.println(file.getName() + ":" + file.delete());
}
}
System.out.println(srcFolder.getName() + ":" + srcFolder.delete());
}
}
}

public class TimerTest {
public static void main(String[] args) throws ParseException {
Timer t = new Timer();

String s = "2014-11-27 15:45:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(s);

t.schedule(new DeleteFolder(), d);
}
}





总结:

多线程(理解)
(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁
(2)死锁问题的描述和代码体现
(3)生产者和消费者多线程体现(线程间通信问题)
以学生作为资源来实现的

资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo

代码:
A:最基本的版本,只有一个数据。
B:改进版本,给出了不同的数据,并加入了同步机制
C:等待唤醒机制改进该程序,让数据能够实现依次的出现
wait()
notify()
notifyAll() (多生产多消费)
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中
(4)线程组
(5)线程池
(6)多线程实现的第三种方案
(7)多线程的面试题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值