多个线程在操作共享的数据且操作共享数据的线程代码有多条。当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。就会导致线程安全问题的产生。
同步代码块原理:将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。
class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) //这个用同一对象锁 只能创建一个对象 { if(s==null) s = new Single(); } } return s; } }
同步锁分为同步代码块锁、同步函数锁,同步函数的使用的锁是this;同步代码块的锁是任意的对象。使用最多的一般是同步代码块。
使用wait、notify、notifylAlll与同步锁配合的应用,wait()将线程添加到线程池中。notify释放本对象线程池中的任意一个线程,notifyAll释放本线程池中所有的线程
class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name)// { while(flag) //唤醒时 重新判断flag 防止flag不符合也往下运行 try{this.wait();}catch(InterruptedException e){} //在这里唤醒 醒后上面判断flag this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"."+this.name); flag = true; notifyAll(); //唤醒所有进程 防止 出现死锁 但是 唤醒所有锁 导致重新判断 效率下降了 } public synchronized void out() { while(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".........."+this.name); flag = false; notifyAll(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("here"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
同步锁如果操作不好会带来新问题:死锁。
线程a、线程b都一直在while(1)中运行,当线程a持有锁a要调用锁b时,如果此时cpu进行线程切换换到线程b,线程b运行,线程b持有锁b要调用锁a,结果发现,锁a被线程a持有没法调用就会等待系统调度切换到线程a,线程a继续运行要调用锁b,发现锁b被b线程持有,也没法运行,两者就形成了死锁。
class Test implements Runnable { private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if(flag) { while(true) synchronized(MyLock.locka) //同步锁a { System.out.println(Thread.currentThread().getName()+"1 locka."); synchronized(MyLock.lockb){ System.out.println(Thread.currentThread().getName()+"1 lockb.."); } } } else { while(true) synchronized(MyLock.lockb) //同步锁b { System.out.println(Thread.currentThread().getName()+"2 lockb"); synchronized(MyLock.locka) { System.out.println(Thread.currentThread().getName()+"2 locka."); } } } } } class MyLock //锁对象 { public static final Object locka = new Object(); //a锁 public static final Object lockb = new Object(); //b锁 } class DeadLockTest { public static void main(String[] args) { Test a = new Test(true); Test b = new Test(false); Thread t1 = new Thread(a); //线程a Thread t2 = new Thread(b); //线程b t1.start(); t2.start(); } }
由于synchronized可能会导致死锁,而防止死锁的notifyAll释放所有进程,导致效率下降,java引入lock锁,Lock接口:替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。
同时更为灵活。可以一个锁上加上多组监视器。lock():获取锁。unlock():释放锁,通常需要定义finally代码块中。
import java.util.concurrent.locks.*; class Resource { private String name; private int count = 1; private boolean flag = false; Lock lock = new ReentrantLock(); //创建锁对象 Condition producer_con = lock.newCondition(); //一把锁可以挂多个监视器 Condition consumer_con = lock.newCondition(); public void set(String name) { lock.lock(); //上锁 try { while(flag) try{producer_con.await();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"."+this.name); flag = true; consumer_con.signal(); //激活消费者线程 这样就可以提高效率 } finally { lock.unlock(); //解除锁 } } public void out() { lock.lock(); try { while(!flag) try{cousumer_con.await();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"..."+this.name); flag = false; producer_con.signal(); //激活生产者线程 } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("provide"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
在lock锁中,一个锁可以添加多个condition监视器,确保可以对指定线程进行操作,提高了运行效率,await()挂起线程 signal()恢复线程。
wait与sleep区别
1,wait可以指定时间也可以不指定。sleep必须指定时间。
2,在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。sleep:释放执行权,不释放锁。
线程终止:控制循环通常就用定义标记来完成。如果线程处于了冻结状态,无法读取标记,
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。 当强制动作会发生了InterruptedException,记得要处理
class StopThread implements Runnable { private boolean flag = true; public synchronized void run() { while(flag) //线程终止 标志 { try { wait(); } catch (InterruptedException e) //interrupt强制唤醒 产生的异常 { System.out.println(Thread.currentThread().getName()+"....."+e); flag = false; } System.out.println(Thread.currentThread().getName()+"......++++"); } } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.setDaemon(true); //守护线程 如果没有别的线程 会自动退出 t2.start(); int num = 1; for(;;) { if(++num==50) { t1.interrupt(); // t2.interrupt(); //如果没设置为守护线程 需要这句话来 退出 break; } System.out.println("main...."+num); } System.out.println("over"); } }
setDaemon()守护线程,必须在线程前开启前设置线程为守护线程,守护线程后台运行,当没有前台线程时,守护线程会自动结束然后结束jvm虚拟机。
jion()函数,线程挂起,直到该线程运行完,挂起此线程的线程才运行。