开始时间:2018年8月30日15:28:19
结束时间:
累计时间:昨天上课两小时+
一:多线程:
进程: 内存单元:
线程: 内存单元当中的一条执行单元:
实现多线程的方式:
(1)extends Thread类: 任务和对象。
(2)implements Runnable : 封装了线程任务: run
Thread t= new Thread(任务对象);
售票:
错误的数据:
原因: (1)有共享数据:
(2)多条线程对共享的数据进行了相关的数学运算:
解决: 同步块:
语法:
synchronized(锁对象){
共享资源
}
优点: 解决数据安全的问题:
弊端: 效率低。
同步的前提:
(1)多个线程之间必须使用同一把锁。
需求:
两个客户到一个银行去存钱,每个客户一次存100,存3次。
问题: 该程序是否有线程安全问题发生? 如果有,写出分析过程。 定义解决方案。
运行程序的结果:
sum=200
sum=200
sum=400
sum=400
sum=500
sum=600
数据的安全问题: 数据的安全问题发生。
原因:
(1)共享数据: sum
(2)对共享数据进行了操作: sum+=100;
数据安全问题的解决:
将共享资源加入同步块:
synchronized (){
}
二: 解决数据安全问题:
方式二:同步函数:
语法:
访问权限修饰符 synchronized 返回值 方法的名称(){
}
同步函数: 同步函数会将函数内部所有的内容,都加锁。
同步函数上加的锁: this 锁: 因为: 同步函数必须被对象调用:
验证: 验证同步方法上的锁 this锁:
三: 静态同步函数上的锁:
静态同步函数上的锁 字节码文件对象:Class :
类名.class
四: 同步函数和同步块的异同点:
相同点: 都能解决数据安全问题:
不同点:
(1)语法上不同:
(2) 同步函数上的锁:只能是this锁:
同步块上的锁对象可以是任意锁对象。
(3)同步函数: 将整个函数当中所有的内容都加锁:
同步块: 索取的对象自由。
(4)同步块比同步函数灵活,所以建议大家使用同步块。
(5)弊端: 同步函数不灵活, 同步块层级深:
什么时候使用同步块 或者是同步函数?
当线程当中只使用到了一个锁对象,可以使用同步函数。
当线程当中使用了多个锁对象,必须使用同步块。
/*
* (1)使用同步块解决数据安全:
*
* (2) 使用同步函数解决数据的安全问题:
*
* (3)验证 同步函数的锁 是this锁:
* 场景: ticket当中定义一个标志位:
*
* run方法当中:
* if(flag){
* //同步块售票
* }else{
* //同步函数:
* }
*
* 当同步块和同步函数都存在的时候,出现了安全问题,说明使用不是同一把锁。
* 当同步块使用的是this锁, 次数数据安全问题得到解决。
* 结论:同步函数和同步块上使用的是用一把锁,从而验证了 同步函数上的锁是this锁。
*
* Thread-0 0 结果:
*
*/
class Ticket implements Runnable {
private static int tickets =1000;
Object obj = new Object();
//定义一个标志位:
boolean flag = true;
//使用同步函数:
/*public synchronized void sale(){
if(tickets>0){//说明邮票 当tickets=1;
// 1 获得了锁对象:
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+ tickets--);
}
}*/
//静态的同步函数: 锁对象不是this。 Ticket.class
public static synchronized void sale(){//字节码文件对象:
if(tickets>0){//说明邮票 当tickets=1;
// 1 获得了锁对象:
/*try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName()+" "+ tickets--);
}
}
@Override
public void run() {
//定义循环;
if(flag){//使用同步块卖票:
while(true){
synchronized (this) {// 静态方法: Ticket.class 静态方法上的默认锁。
// 1 : cpu的执行权限,并且获得了锁对象。
if(tickets>0){//说明邮票 当tickets=1;
// 1 获得了锁对象:
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+ tickets--);
}
}
}
}else{//使用同步方法卖
while(true){
sale();
}
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) throws InterruptedException {
//创建线程任务对象:
Ticket t= new Ticket();//封装了任务:
//创建线程对象:
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
//由于CPU的切换速度很快, 开启了一条线程后,t1还没来记得售卖, 将标志位设置为false。
// 导致只有一天线程在这儿售票:
Thread.sleep(10);
//改变标志位:
t.flag=false;
t2.start();
}
}
五: 面试问题:
单例模式:数据安全问题:
饿汉模式:不会出现数据安全问题:
懒汉模式:
* 懒汉模式: 在多线程的环境当中会出现数据安全问题:
* 原因:
* 共享资源: s这个对象:
* 对s这个对象进行了操作。
*
* 解决: 同步块解决:
*public static Singleton1 getInstance(){
// 1 2 3 4 5 6 7 8
if(s==null){
// 1 2 3 4
synchronized (Singleton1.class) {
if(s==null){
// 1
s= new Singleton1();
}
}
}
return s;
}
* 建议: 单例模式:使用饿汉模式:
package com.yidongxueyuan.thread;
//单例模式:
// 饿汉模式: 不会出现数据安全问题:
class Singleton {
private static final Singleton s= new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return s;
}
}
//懒汉模式
class Singleton1 {
private static Singleton1 s;// null
private Singleton1(){
}
// 1 2 3 4
public static Singleton1 getInstance(){
// 1 2 3 4 5 6 7 8
if(s==null){
// 1 2 3 4
synchronized (Singleton1.class) {
if(s==null){
// 1
s= new Singleton1();
}
}
}
return s;
}
/*
* 懒汉模式: 在多线程的环境当中会出现数据安全问题:
* 原因:
* 共享资源: s这个对象:
* 对s这个对象进行了操作。
*
* 解决: 同步块解决:
*
*/
}
public class ThreadDemo03 {
}
六: 不能在run方法上加锁:
原因: run方法当中封装了线程任务,需要被多条线程同时操作,提高效率。
如果直接在run方法上加锁, 相当于只能有一条线程操作run方法,此时多线程
编程了单线程。
关于娱乐圈分手月随便说几句
例: 两个线程等待运行,一个线程占了一根筷子, 运行需要两根筷子。出现死锁。
在下面程序中, 上面的线程握住sb1的锁,睡眠之后 执行下面的线程,下面线程握住sb2的锁,
上面的线程执行完毕需要获得sb2的锁 被下面线程握住。
同理下面线程执行完毕需要的锁被上面线程握住。
两个线程都需要对方释放自己的资源,才能运行。
反观恋爱
1 在一起的时候都不愿先开口 ,谁先开口谁就输了。
2 发现不合适的的时候还是不愿意先开口,我觉得能互相成全最好,与其死耗着那不如我当这个罪人好了。
娱乐圈分手的这么多,与其为之惋惜,不如为其祝福。
下面一个简单的典型死锁
//死锁的问题:处理线程同步时容易出现。
//不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
//写代码时,要避免死锁!
public class TestDeadLock {
static StringBuffer sb1 = new StringBuffer();
static StringBuffer sb2 = new StringBuffer();
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized (sb1) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread() {
public void run() {
synchronized (sb2) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("C");
synchronized (sb1) {
sb2.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
主要是线程交叉的时候出现sleep的情况 会出现死锁多一些吧。
下例程序实现了线程通信
/*
* 生产者/消费者问题
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
* 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,
* 如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,
* 如果店中有产品了再通知消费者来取走产品。
分析:
1.是否涉及到多线程的问题?是!生产者、消费者
2.是否涉及到共享数据?有!考虑线程的安全
3.此共享数据是谁?即为产品的数量
4.是否涉及到线程的通信呢?存在这生产者与消费者的通信
*/
class Clerk{//店员
int product;
public synchronized void addProduct(){//生产产品
if(product >= 20){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
product++;
System.out.println(Thread.currentThread().getName() + ":生产了第" + product + "个产品");
notifyAll();
}
}
public synchronized void consumeProduct(){//消费产品
if(product <= 0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName() + ":消费了第" + product + "个产品");
product--;
notifyAll();
}
}
}
class Producer implements Runnable{//生产者
Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
public void run(){
System.out.println("生产者开始生产产品");
while(true){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.addProduct();
}
}
}
class Consumer implements Runnable{//消费者
Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
public void run(){
System.out.println("消费者消费产品");
while(true){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class TestProduceConsume {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer p1 = new Producer(clerk);
Consumer c1 = new Consumer(clerk);
Thread t1 = new Thread(p1);//一个生产者的线程
Thread t3 = new Thread(p1);
Thread t2 = new Thread(c1);//一个消费者的线程
t1.setName("生产者1");
t2.setName("消费者1");
t3.setName("生产者2");
t1.start();
t2.start();
t3.start();
}
}
生产者和消费者问题有时候会出现连续生产 或者连续消费问题
/*
* 连续生产:
* 1 2 3 4
* flag = false;
* 1 --- 1 flag = true
* 1 --- wait
* 2 ----wait
* 3-----消费了1号 false 唤醒 1 ---->活
* 3-----wait
* 4-----wait
*
* 1-----> 生产商品: 2 true 。 2 ----> 活了:
* 1-----》 wait
* 2-----》 被唤醒后,没有判断标志位: 执行向下执行, 生产了3 号商品、。
* 出现了连续的生产:
*
*
*
*/
*
* 单生产和单消费的案例:
*/
/*
* 描述资源:
*/
class Resource{
private String name; //商品的名称:
private int count=1; // 标识商品的编号(只能自增)
//定义一个标志位: 表示盘子当中是否有商品:
boolean flag = false;
//提供生产的方法:
public void set(String name){
synchronized (this) {
while(flag){
try {
this.wait();//生产者等待: 1 2
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name= name+ count; //生产出来的商品带有名称切带有编号;
count++; //商品没生产一次,编号自增一次。
System.out.println(Thread.currentThread().getName()+"...生产了"+this.name);
// 该标志位:
flag= true; //有东西:
// this.notify();//唤醒:
this.notifyAll();
}
}
//提供消费的方法:
public void out() {
synchronized (this) {
while(!flag){//指定了: 表明盘子当中没有东西, 消费者等待:
try {
this.wait();// 2
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费了产品:
System.out.println(Thread.currentThread().getName()+"......消费了...."+this.name);
//改变标志位;
flag = false; //没商品:
//唤醒生产者生产:
// this.notify();
this.notifyAll();
}
}
}
//线程任务: 描述生产者的任务:
class Productor implements Runnable{
// 引入资源:
private Resource r;
//为了让消费者和生产者保证操作的对象唯一:
public Productor(Resource r){
this.r= r;
}
public void run() {
while(true){
r.set("面包");//循环生产:
}
}
}
//线程任务: 描述消费者:
class Consumer implements Runnable{
// 引入资源:
private Resource r;
//为了让消费者和生产者保证操作的对象唯一:
public Consumer(Resource r){
this.r= r;
}
public void run() {
while(true){
r.out();//循环消费
}
}
}
public class ThreadDemo04 {
public static void main(String[] args) {
//创建任务共享的资源:
Resource r = new Resource();
// 创建生产线程任务:
Productor pro = new Productor(r);
//创建消费者的线程任务:
Consumer con = new Consumer(r);
//创建线程对象:
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
//开启“:
t1.start();
t2.start();
t3.start();
t4.start();
/*
* 1 2 3 4
* 1:
*
*/
}
}
如果直接将if 换成while , 被唤醒的线程会重新判断标志位; 此时容易死锁:
使用Object当中提供的notifyAll方法,唤醒所有的等待线程。 此时问题解决:
但是还是每次会有1/3的概率造成重复判断效率很低
2018年8月13日11:32:16 于易动
如果真的喜欢 ,编程语言会有趣味的。一直感觉java 是有哲理的语言。