Java day13

一:线程互斥:多线程同时操作同一个对象
a)很多ATM-----》每台ATM都会同时在进行工作。就相当于多线程。
卡–>主卡,副卡(同一个账号)
//取钱withdraw
//存钱deposit

b)synchronized(共享区){临界区}
共享区:多线程同时操作同一个对象
临界区:多线程对共享区进行操作的代码区
synchronized可以放在方法上,就是一个同步方法。
synchronized可以放在代码上,就是一个同步代码块。

c)并发访问的线程加上同步锁之后就称为线程互斥。
加同步锁,加在共享对象上。
每一个对象都有唯一的一把锁。
lock对象锁---->执行代码------>unlock对象锁

谁抢到了对象锁的线程就先执行。
没有抢到对象锁的线程就等待对象锁。

d) 将所有影响的方法都定义成线程安全的方法就变成了线程安全的类。
线程安全的类—效率低
如Hashtable类

e)什么情况下会释放对象锁:
1)当前线程的同步方法,同步代码块执行结束,
当前线程立即释放对象锁
2)当线程在同步代码块,同步方法中遇到break,
return终止了该代码块,该方法的继续执行,当前
线程将会释放对象锁
3)当线程在同步代码块,同步方法中出现未处理的
Error或Exception,导致了该代码块,该方法异常结束
时将会释放对象锁
4)当线程执行同步代码块,同步方法时,程序执行了
对象锁对象的wait()方法,则当前线程暂停,并释放对象锁。

f)什么情况下,线程不会释放对象锁:
1)线程执行同步代码块或者同步方法时,程序调用Thread.sleep()
方法。Thread.yield方法来暂停当前线程的执行,当前
线程不会释放对象锁。
2)线程执行同步代码块时,其他线程调用了该线程的suspend
方法将该线程挂起,该线程不会释放对象锁,当前,
我们应该尽量避免使用suspend和resume方法来控制线程。

g)同步锁Lock
从jdk1.5之后,Java提供了另外一种线程同步的机制:
它通过显示定义同步锁对象来实现同步,在这种机制下,
同步锁应该使用Lock对象充当。
通常认为:Lock提供了比synchronized方法和synchronized
代码块更广泛的锁定操作,Lock实现允许更灵活的结构,
可以具有差别很大的属性,并且可以支持多个相关的Condition对象,
Lock是控制多个线程对共享资源进行访问的工具。通常,
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock
对象加锁,线程开始访问共享资源之前应先获得Lock对象,不过,
某些锁可能允许对共享资源并发访问。比如ReadWriteLock读写锁。
当前,在实现线程安全的控制中,通过喜欢使用ReentrantLock可重入锁,
使用该Lock对象可以显式加锁,释放锁。
使用Lock对象的代码格式为:
class Test{
private final ReentrantLock lock=new ReentrantLock();
public void test(){
lock.lock();//加锁
try{
//…需要保证线程安全的代码
}finally{
//使用finally块来保证释放锁
lock.unlock();
}
}
}
h)Lock与使用同步方法有点相似,只是使用Lock时显式使用Lock
对象作为同步锁,而使用同步方法时系统隐式使用当前对象作为
对象锁,同样符合“加锁—访问—释放锁"的操作模式,而且
使用Lock对象时每个Lock对象对应一个当前对象,一样可以保证
对于同一个对象,同一时刻只能有一个线程进入临界区。
使用同步方法和同步代码块的机制使得多线程安全编程非常方便。
并且也可以避免很多涉及锁的常见编程错误,但是Lock是更加灵活
的方式,并且Lock还提供了其他的功能。

二:线程的通信—>线程同步(操作的也是同一个对象)
线程互斥谁先抢到对象锁就谁先执行(如抛绣球)。
线程同步会控制线程执行的先后顺序。
线程同步线程会有依赖关系。
一个boy存钱,一个girl取钱。必须先存钱后取钱。就是线程同步。
重复(存钱---->取钱)
存钱之后通知可以取钱。
取钱之后通知可以存钱。
并且是先存后取
男:标志位flag = false;存钱 flag=true;
女:标志位flag = true;取钱 flag=false;
模拟生成者和消费者—针对产品
生成者:从1到100累加的和作为产品。
消费者:打印该产品。
线程通信的步骤:
1)找到共有对象。
2)wait(),notify(),notifyAll()—Object类中的方法。
需要对同一个资源等待,唤醒才有效。
是针对对象进行等待和通知。这些方法一定要放在
synchronized关键字中,用来保护共有资源。
生产者生成产品之后通知消费者–notify
消费者等待生产者生产产品—wait
3)保证wait()执行在notifyAll()方法的前面.使用标志变量。
notifyAll()之前先判断有没有线程在等待。
如果有就通知,没有就小睡一下只有有等待线程出现。

sleep():不会释放对象锁。
wait():让当前线程进入等待状态,会释放对象锁。
notify():通知等待中的某个线程进入到就绪状态。会释放对象锁。
notifyAll():通知等待中的所有线程进入到就绪状态。会释放对象锁。
可以通过公交车和人和师傅来说明

三:线程的死锁
多个线程同时操作多个对象很容易产生死锁。
解决方式:给多个对象加对象锁的时候。
将加锁的顺序保持一致,就可以避免死锁。

四:Thread类中yield()方法
yield():它是一个Thread类提供的一个静态方法,它可以
让当前正在执行的线程暂停,但它不会阻塞该线程,
他只是将该线程转入就绪状态,yield只是让当前线程
暂停一下,让系统的线程调度器重新调度一次,完成
可能的情况是:当某个线程调用了yield方法暂停之后,
线程调度器又将其调度出来重新执行。
实际上,当某个线程调用了yield方法暂停之后,只有
优先级与当前线程相同,或者优先级比当前线程更高
的就绪状态的线程才有获取执行的机会。如果当前没
有比自己优先级还高的线程就没有效果。

关于sleep方法和yield方法的区别如下:
1)sleep方法暂停当前线程后会给其他线程执行机会,
不会理会其他线程的优先级。但yield方法只会给优先级
相同,或优先级更高的线程执行机会。
2)sleep方法会将线程转入阻塞状态,指导经过阻塞时间
才会转入就绪状态,而yield不会将线程转入阻塞状态,
它只是强制当前线程进入就绪状态,因此完全有可能某个
线程调用yield方法暂停之后,立即再次获取处理器资源被执行。
3)sleep方法申明抛出了InterrupedException异常,所以
调用sleep方法是要么捕捉该异常,要么显式申明抛出该异常,
而yield方法泽没有申明抛出任何异常。
4)sleep方法比yield方法有更好的可移植性,通常不要依靠
yield来控制并发线程的执行。

五:改变线程的优先级
每个线程执行时都具有一定的优先级,优先级高的线程获得较多
的执行机会,而优先级低的线程则获得较少的执行机会,每个
线程默认的优先级都与创建他的父线程具有相同的优先级。
在默认情况下,main线程具有普通优先级,由main线程创建的
子线程也有普通优先级。
Thread类提供了setPrioirity和getPriority方法来设置和返回
指定线程的优先级,其中setPrioirty方法的参数是一个整数,
范围是1到10之间,也可以使用Thread类的三个静态常量:
MAX_PRIORITY:其值是10
MIN_PRIORITY:其值是1
NORM_PRIORITY:其值是5
setPrioirity(int)设置线程的优先级.整数越大优先级越高。
注意:虽然Java提供了10个优先级别,但这些优先级别需要操作系统
的支持,不幸的是,不同的操作系统上优先级并不相同,而且也不能
很好地和Java的10个优先级对应,比如windows2000仅提供了7个优先级,
在这种情况,我们应该尽量避免直接为线程指定优先级,而应该使用
线程的三个静态常量来设置优先级,这样才可以保证程序具有最好的
可移植性。

六:后台进程
有一种线程,他是在后台运行的,他的任务是为其他的线程提供服务,
这种线程被称为“后台线程”(Daemon Thread),又称“守护线程”,
或“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。
后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡,
调用Thread对象setDamon(true)方法可将指定线程设置为后台线程,
当整个虚拟机中只剩下后台线程时,程序就没有继续运行的必要了,
所有虚拟机也就退出了。
主线程默认是前台线程,前台线程创建的子线程默认是前台线程,
后台线程创建的子线程默认是后台线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值