一、线程的同步与死锁
2.同步处理
指的是所有的线程不是一起进入到方法中执行,而是按照顺序一个一个进来。
(1)synchronized处理同步问题
使用synchronized可以实现这把“锁”,它有两种模式:同步代码块、同步方法。
·使用同步代码块:
实际上,synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的代码段。即synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。
当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才能达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。
锁住这段代码,有两种方法:
·锁住同一个对象
static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。
3.Lock锁
4.死锁
同步的本质在于,一个线程等待另一个线程执行完毕后才可以继续执行。但如果现在相关的几个线程都在等待着,那么就会造成死锁。死锁一旦出现,整个程序就将中断执行,所以死锁属于严重性问题。过多的同步会造成死锁,对于资源的上锁一定注意不要成“环”。
二、wait()方法和notify()方法
1.wait()方法
wait()方法就是使线程停止运行。
(1)方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法是用来将线程置入“预执行队列”中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。
(2)wait()方法只能在同步方法中或同步块中调用。如果调用wait()时,没有持有适当的锁,会抛出异常。
(3)wait()方法执行后,当前线程释放锁,线程与其他线程竞争重新获取锁。
2.notify()方法
notify()方法就是使停止的线程继续运行。
(1)方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其他线程,对其发出通知notify,并使它们重新获取该对象的对象锁。如果有多个线程等待,则有线程规划器随机挑选出一个呈wait状态的线程。
(2)在notify方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。
注意:wait、notify必须使用在synchronized同步方法或者代码块内。
3.notifyAll()方法
notify()方法只是唤醒一个线程,notifyAll()方法可以一次唤醒所有的等待线程。
三、线程池
1.
对于线程池操作的核心类和接口就定义在java.util.conurrent包中,这里面有两个核心接口:
(1)普通线程池:public interface ExecutorService extends Executor
(2)调度线程池:public interface ScheduledExecutorService extends ExecutorService
如果要进行线程池的创建,一般可以使用Executors类完成
(1)创建无大小的线程池:public static ExecutorService newCachedThreadPool()
(2)创建固定的大小的线程池:public static ExecutorService newFixedThreadPool(int nThreads)
(3)单线程池:public static ExecutorService newSingleThreadExecutor()
(4)创建定时调度池:public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
1.如:多个线程同时卖票
class MyThread implements Runnable{ private int ticket = 10; @Override public void run() { while(this.ticket > 0) { try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + "张票"); } } } public class Test{ public static void main(String[] args) { MyThread myThread = new MyThread(); new Thread(myThread,"A").start(); new Thread(myThread,"B").start(); new Thread(myThread,"C").start(); } }经过多次运行,票数有可能会出现负数,这就是不同步。不同步的唯一好处就是处理速度快(多个线程并发执行)。
2.同步处理
指的是所有的线程不是一起进入到方法中执行,而是按照顺序一个一个进来。
(1)synchronized处理同步问题
使用synchronized可以实现这把“锁”,它有两种模式:同步代码块、同步方法。
·使用同步代码块:
如果要使用同步代码块必须设置一个要锁定的对象,所以一般可以锁定当前对象:this
class MyThread implements Runnable{ private int ticket = 100; @Override public void run() { for(int i = 0;i < 1000;i++) { synchronized (this) { if(this.ticket > 0) { try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + "张票"); } } } } } public class Test{ public static void main(String[] args) { MyThread myThread = new MyThread(); new Thread(myThread,"A").start(); new Thread(myThread,"B").start(); new Thread(myThread,"C").start(); } }
·同步方法
class MyThread implements Runnable{ private int ticket = 1000; @Override public void run() { // TODO Auto-generated method stub for(int i = 0;i < 1000;i++) { this.sale(); } } public synchronized void sale() { if(this.ticket > 0) { try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + "张票"); } } } public class Test{ public static void main(String[] args) { MyThread myThread = new MyThread(); new Thread(myThread,"A").start(); new Thread(myThread,"B").start(); new Thread(myThread,"C").start(); } }同步可以保证数据的完整性(线程安全操作),但是其执行的速度会很慢。
实际上,synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的代码段。即synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。
当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才能达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。
锁住这段代码,有两种方法:
·锁住同一个对象
如:
class Sync { public void test() { synchronized (this) { System.out.println("test方法开始,当前线程为 " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test方法结束,当前线程为 " + Thread.currentThread().getName()); } } } class MyThread extends Thread{ private Sync sync; public MyThread(Sync sync) { this.sync = sync; } @Override public void run() { this.sync.test(); } } public class Test{ public static void main(String[] args) { Sync sync = new Sync(); for(int i = 0;i < 3;i++) { MyThread myThread = new MyThread(sync); myThread.start(); } } }·让synchronized锁这个类对应的class对象
如:
class Sync { public void test() { synchronized (Sync.class) { System.out.println("test方法开始,当前线程为 " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test方法结束,当前线程为 " + Thread.currentThread().getName()); } } } class MyThread extends Thread{ @Override public void run() { Sync sync = new Sync(); sync.test(); } } public class Test{ public static void main(String[] args) { for (int i = 0; i < 3; i++) { MyThread myThread = new MyThread(); myThread.start(); } } }上面的synchronized(Sync.class)实现了全局锁的效果。因此,如果想要锁的是代码段,锁住多个对象的同一方法,使用这种全局锁,锁的是类而不是this。
static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。
3.Lock锁
如:使用ReentrantLock进行同步处理
class Mythread implements Runnable { private int ticket = 500; private Lock ticketLock = new ReentrantLock(); @Override public void run() { for (int i = 0; i < 500; i++) { ticketLock.lock(); if (this.ticket > 0) { try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + "张票"); } ticketLock.unlock(); } } } public class Test { public static void main(String[] args) { Mythread mythread = new Mythread(); new Thread(mythread, "A").start(); new Thread(mythread, "B").start(); new Thread(mythread, "c").start(); } }在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。到了JDK1.6,对synchronized加入了很多优化措施,导致在JDK1.6中synchronized的性能并不比Lock差,所以,优先考虑使用synchronized。
4.死锁
同步的本质在于,一个线程等待另一个线程执行完毕后才可以继续执行。但如果现在相关的几个线程都在等待着,那么就会造成死锁。死锁一旦出现,整个程序就将中断执行,所以死锁属于严重性问题。过多的同步会造成死锁,对于资源的上锁一定注意不要成“环”。
二、wait()方法和notify()方法
1.wait()方法
wait()方法就是使线程停止运行。
(1)方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法是用来将线程置入“预执行队列”中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。
(2)wait()方法只能在同步方法中或同步块中调用。如果调用wait()时,没有持有适当的锁,会抛出异常。
(3)wait()方法执行后,当前线程释放锁,线程与其他线程竞争重新获取锁。
如:观察wait()方法使用
public class Test{ public static void main(String[] args) throws InterruptedException { Object object = new Object(); synchronized (object) { System.out.println("等待中"); object.wait(); System.out.println("等待已过"); } System.out.println("main方法结束"); } }这样在执行到object.wait();之后就一直等待下去。
2.notify()方法
notify()方法就是使停止的线程继续运行。
(1)方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其他线程,对其发出通知notify,并使它们重新获取该对象的对象锁。如果有多个线程等待,则有线程规划器随机挑选出一个呈wait状态的线程。
(2)在notify方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。
如:使用notify()方法唤醒线程
class MyThread implements Runnable { private boolean flag; private Object object; public MyThread(Boolean flag, Object object) { this.flag = flag; this.object = object; } public void waitMethod() { synchronized (object) { try { while (true) { System.out.println("wait()方法开始:" + Thread.currentThread().getName()); object.wait(); System.out.println("wait()方法结束:" + Thread.currentThread().getName()); return; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void notifyMethod() { synchronized (object) { System.out.println("notify()方法开始:" + Thread.currentThread().getName()); object.notify(); System.out.println("notify()方法结束:" + Thread.currentThread().getName()); } } @Override public void run() { if (flag) { this.waitMethod(); } else { this.notifyMethod(); } } } public class Test{ public static void main(String[] args) throws InterruptedException { Object object = new Object(); MyThread waitThread = new MyThread(true, object); MyThread notifyThread = new MyThread(false, object); Thread thread1 = new Thread(waitThread,"wait线程"); Thread thread2 = new Thread(notifyThread,"notify线程"); thread1.start(); Thread.sleep(1000); thread2.start(); System.out.println("main方法结束"); } }第一个线程执行的是waitMethod方法,该方法里面有个死循环并且使用了wait方法进入等待状态将释放锁,如果这个线程不被唤醒的话将会一直等待下去,这个时候第二个线程执行的是notifyMethod方法,该方法里面执行了一个唤醒线程的操作,并且一直将notify的同步代码块执行完毕之后才会释放锁然后继续执行wait结束打印语句。
注意:wait、notify必须使用在synchronized同步方法或者代码块内。
3.notifyAll()方法
notify()方法只是唤醒一个线程,notifyAll()方法可以一次唤醒所有的等待线程。
如:使用notifyAll()方法唤醒所有线程
class MyThead implements Runnable{ private boolean flag; private Object object; public MyThead(boolean flag,Object object) { this.flag = flag; this.object = object; } public void waitMethod() { synchronized (object) { try { while(true) { System.out.println("wait()方法开始:" + Thread.currentThread().getName()); object.wait(); System.out.println("wait()方法结束:" + Thread.currentThread().getName()); return; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void notifyAllMethod() { synchronized (object) { System.out.println("notifyAll()方法开始:" + Thread.currentThread().getName()); object.notifyAll(); System.out.println("notifyAll()方法结束:" + Thread.currentThread().getName()); } } @Override public void run() { if(flag) { this.waitMethod(); }else { this.notifyAllMethod(); } } } public class Test{ public static void main(String[] args) throws InterruptedException { Object object = new Object(); MyThead waitThread1 = new MyThead(true, object); MyThead waitThread2 = new MyThead(true, object); MyThead waitThread3 = new MyThead(true, object); MyThead notifyAllThread = new MyThead(false, object); Thread thread1 = new Thread(waitThread1,"wait线程A"); Thread thread2 = new Thread(waitThread2,"wait线程B"); Thread thread3 = new Thread(waitThread3,"wait线程C"); Thread thread = new Thread(notifyAllThread,"notifyAll线程"); thread1.start(); thread2.start(); thread3.start(); Thread.sleep(1000); thread.start(); System.out.println("main方法结束"); } }注意:唤醒线程不能过早,如果还没有线程在等待中时,过早的唤醒线程,这个时候就会出现先唤醒再等待的效果了。
三、线程池
1.
对于线程池操作的核心类和接口就定义在java.util.conurrent包中,这里面有两个核心接口:
(1)普通线程池:public interface ExecutorService extends Executor
(2)调度线程池:public interface ScheduledExecutorService extends ExecutorService
如果要进行线程池的创建,一般可以使用Executors类完成
(1)创建无大小的线程池:public static ExecutorService newCachedThreadPool()
(2)创建固定的大小的线程池:public static ExecutorService newFixedThreadPool(int nThreads)
(3)单线程池:public static ExecutorService newSingleThreadExecutor()
(4)创建定时调度池:public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
2.如:创建无大小限制的线程池
public class Test{ public static void main(String[] args) { ExecutorService newCachedThreadPool =Executors.newCachedThreadPool(); for(int i = 0;i < 10;i++) { int index = i; newCachedThreadPool.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ",i = " + index); } }); } newCachedThreadPool.shutdown(); } }
如:创建单线程的线程池
public class Test{ public static void main(String[] args) { ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { int index = i; newSingleThreadExecutor.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ",i = " + index); } }); } newSingleThreadExecutor.shutdown(); } }
如:创建固定大小线程池
public class Test{ public static void main(String[] args) { ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3); for(int i = 0;i < 10;i++) { int index = i; newFixedThreadPool.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ",i = " + index); } }); } newFixedThreadPool.shutdown(); } }
如:创建定时调度池
public class Test{ public static void main(String[] args) { ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2); for(int i = 0;i < 10;i++) { int index = i; newScheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ",i = " + index); } }, 3, 2, TimeUnit.SECONDS); //3s后开始执行,每间隔2s执行一次,单位为秒(s) } } }线程池带来的好处是多个线程按照组的模式进行程序的处理。这样在某些复杂环境下,性能就会得到很好的提升。