7、线程同步
1、多线程共享数据必须使用同步
在多线程的操作中,多个线程有可能同时处理同一个资源,这就是多线程中的共享数据。
这样是不安全的 两个4 两个0
2、线程同步
解决数据共享问题,必须使用同步,所谓同步就是指 多个线程在同一个时间段内只能有一个线程执行指定代码,其他线程要等待此线程完成之后才可以继续执行。
线程进行同步,有以下三种方法:
(1)同步代码块
synchronized(要同步的对象){
要同步的操作 ;
}
把要同步的那段代码 用synchronized的大括号括起来
小括号要填一个对象进去
这样 就在线程里建一个通用对象obj (这个对象写什么都行 保证多个线程使用的是同一个对象就可以 integer啥的都可以 就单纯表示一个锁的意思
或者为了图省事 直接写this 当前对象 这样更方便
)
这样main调用时 t1 t2 使用的就是同一个对象
这样就安全了
(2)同步方法
public synchronized void method(){
要同步的操作 ;
}
或者把需要同步的操作代码 抽象出来 放进 同步 方法里面
在for循环调用method方法
同步方法:同步的对象是当前对象(this)
(3)Lock(ReentrantLock)
就不用synchronized关键字了
使用锁
更加灵活 想什么时候锁 什么时候解锁 都可以操作 或者加判断条件什么的 synchronized做不到这一点
使用ReentrantLock 互斥锁这个类
lock.lock();//锁
lock.unlock();//释放锁
这两句夹的就决定了锁的内容
为了保证一定解锁 可以使用try finally 防止不解锁 避免死锁
3、同步准则
当编写 synchronized 块时,有几个简单的准则可以遵循,这些准则在避免死锁和性能危险的风险方面大有帮助:
(1)使代码块保持简短。把不随线程变化的预处理和后处理移出synchronized 块。(即不需要同步的不要放在synchronize所管 辖的区域 尽量保持简短 多线程共享数据,会有安全问题 ,使用同步可以解决安全问题 ,但同时会牺牲性能,所以同步的代码块要尽量保持简短,把不随数据变化的相关代码移除同步块 )
(2)不要阻塞。如InputStream.read()。
(3)在持有锁的时候,不要对其它对象调用(同步)方法。(在synchronized或者锁的内容里面 不要调用其他对象方法 万一那个对象方法也是同步的 要小心 你等他 他等你 会造成死锁 不会报错 程序死在那了)
- Created by vince on 2017/6/5.
- 1多线程共享数据时,会发生线程不安全的情况
- 2多线程共享数据必须使用同步
- 3实现同步的三种方法:
- (1)使用同步代码块
- (2)使用同步方法
- (3)使用Lock(更灵活的代码控制)
- 多线程共享数据,会有安全问题 ,使用同步可以解决安全问题 ,但同时会牺牲性能,所以同步的代码块要
- 尽量保持简短,把不随数据变化的相关代码移除同步块,不要阻塞
package com.vince;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by vince on 2017/6/5.
* 1多线程共享数据时,会发生线程不安全的情况
* 2多线程共享数据必须使用同步
* 3实现同步的三种方法:
* (1)使用同步代码块
* (2)使用同步方法
* (3)使用Lock(更灵活的代码控制)
* 多线程共享数据,会有安全问题 ,使用同步可以解决安全问题 ,但同时会牺牲性能,所以同步的代码块要
* 尽量保持简短,把不随数据变化的相关代码移除同步块,不要阻塞
*/
public class ThreadDemo4 {
public static void main(String[] args) {
MyRunnable5 mr5 = new MyRunnable5();
Thread t1 = new Thread(mr5);
Thread t2 = new Thread(mr5);
t1.start();
t2.start();
}
}
class MyRunnable5 implements Runnable {
private int ticket = 10;//售票
//private Object obj = new Object();//同步锁
@Override
public void run() {
for (int i = 0; i < 300; i++) {
// if (ticket > 0) {
// synchronized (this) {
// ticket--;
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println("您购买的票剩余" + ticket + "张");
// }
// }
method();
}
}
//同步方法:同步的对象是当前对象(this)
private synchronized void method() {
if (ticket > 0) {
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("您购买的票剩余" + ticket + "张");
}
}
//互斥锁
ReentrantLock lock = new ReentrantLock();
//Lock实现同步
private void method2() {
lock.lock();//锁
try {
if (ticket > 0) {
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("您购买的票剩余" + ticket + "张");
}
}finally {
lock.unlock();//释放锁
}
}
}
死锁
过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才有可能出现。
多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁。
下面举一个死锁的例子 只是个例子 写的时候不要这么写
package com.vince;
/**
* Created by vince on 2017/6/5.
* 线程死锁:在一个同步方法中调用了另一个对象的同步方法,可能产生死锁
*
*/
public class DeadThreadDemo {
public static void main(String[] args) {
new DeadThread();
}
}
//顾客
class Customer{
public synchronized void say(Waiter w){
System.out.println("顾客说:先吃饭再买单!");
w.doService();
}
public synchronized void doService(){
System.out.println("同意了,买完单再吃饭!");
}
}
//服务员
class Waiter{
public synchronized void say(Customer c){
System.out.println("服务员说:先买单再吃饭!");
c.doService();
}
public synchronized void doService(){
System.out.println("同意了,吃完饭再买单!");
}
}
//死锁线程
class DeadThread implements Runnable{
Customer c = new Customer();
Waiter w = new Waiter();
public DeadThread(){
new Thread(this).start();
w.say(c);
}
@Override
public void run() {
c.say(w);
}
}
使用idea不会 功能太强大了 死锁给避免了 笑哭
结果死锁了
线程死锁:在一个同步方法中调用了另一个对象的同步方法,可能产生死锁