传智播客风清扬视频-------线程简介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)多线程的面试题
传智播客风清扬视频-------线程简介2相关推荐
- 传智播客风清扬视频-------线程简介
想了解线程,必须先了解进程,因为线程是依赖进程存在的. 什么是进程? 进程就是正在运行的程序:是系统进行资源分配和调用的独立单位.每一个进程都有它自己的内存空间和系统资源. 多进程有什么意义? 单进程 ...
- 传智播客风清扬视频-------网络编程简介
计算机网络模型 OSI(Open System Interconnection开放系统互连)参考模型 TCP/IP参考模型 OSI 应用层--表示层--会话层--传输层--网络层--数据链路层--物理 ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 卷 ba ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)
卷 backup 的文件夹 PATH 列表 卷序列号为 00000025 D4A8:14B0 J:. │ 1.txt │ c语言经典案例效果图示.doc │ ├─1传智播客_尹成_C语言从菜鸟到 ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)...
卷 backup 的文件夹 PATH 列表 卷序列号为 00000025 D4A8:14B0 J:. │ 1.txt │ c语言经典案例效果图示.doc │ ├─1传智播客_尹成_C语言从菜鸟到 ...
- 传智播客C语言视频第二季 第一季基础上增加诸多C语言案例讲解,有效下载期为10 5-10 10关闭
分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 卷 backup ...
- 传智播客-php基础视频,传智播客PHP核心基础视频教程推荐(资源)
PHP,是英文超级文本预处理语言Hypertext Preprocessor的缩写.PHP 是一种 HTML 内嵌式的语言,是一种在服务器端执行的嵌入HTML文档的脚本语言,语言的风格有类似于C语言, ...
- java工作流 传智播客_Activiti工作流视频教学(企业开发实战讲解)_传智播客
Activiti工作流视频教学(企业开发实战讲解)_传智播客课程简介: Activiti工作流视频教学(企业开发实战讲解)_传智播客本教学共分4天进行讲解,本站提供第1天内容在线观看,全集教学请在本站 ...
- 传智播客软件测试学习视频汇总:
课程名称 分类 URL 提取码 软件测试入门到精通 视频 http://yun.itheima.com/course/490.html?aoe cnj1 资料 https://pan.baidu.co ...
- Java多线程(6)——Thread类中的一些方法(传智播客毕老师视频讲解)
1.守护线程 代码如下: import java.util.concurrent.locks.*; public class StopTest implements Runnable {private ...
最新文章
- python不能创建新变量_Python之变量的创建过程!
- 拥有懂需求的云计算供应商,是一种怎样的体验
- Form表单中的button导致页面刷新而无法进入Ajax请求回调函数
- k8s部署tomcat及web应用_部署 Spring Boot 应用到 K8S 教程
- 熬夜都要看完的 Spring 干货!
- File “/usr/bin/yum“, line 30 及 File “/usr/libexec/urlgrabber-ext-down“, line 28
- 一分钟系列:详解阿里云68款产品【热点问题+用户实践】
- Linux下MongoDB安装和配置详解
- [DFA|有限状态机] leetcode 8 字符串转换整数(atoi)
- 使用js来实现模拟无刷新文件上传。
- Routerboard/DR4019S-Qualcomm-IPQ4019-2T2R-Dual-Band-2-4GHz-5GHz-support-OpenWRT-802.11ac-Wave-2.
- LabVIEW心率监测装置
- cad动态块制作翻转_CAD中怎么定义旋转动态块?
- directx 11 64位
- 37岁Java程序员求职6K+职位被怼,你怎么看?
- 犹他州计算机科学,犹他州大学计算机科学computer science专业排名第201~250名(2020THE泰晤士高等教育世界大学排名)...
- 均值和方差的计算(已知两样本标准差,求总体标准差)
- 本地消息表(异步确保)
- 微信小程序—自定义组件
- 网络地址转换协议NAT功能详解+NAT配套练习题