简介: 在我们实际的开发过程中,线程带来的安全问题不容我们忽视。cpu随机切换线程,我们希望cpu可以按照我们的意愿进行执行,此时就需要线程之间的通讯了。
线程同步
(1)线程安全问题
我们用代码模拟火车票窗口买票:
public class myText implements Runnable{
private int tickNum=100; //一百张
public static void main(String[] args)
{
myText ticket=new myText(); //售票中心
Thread thread1=new Thread(ticket,"窗口1");
Thread thread2=new Thread(ticket,"窗口2");
Thread thread3=new Thread(ticket,"窗口3");
thread1.start();
thread2.start();
thread3.start();
}
@Override
public void run() {
while (true){
if(tickNum>0){
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售出第"+tickNum-- +"张票");
}
}
}
}
出现线程安全问题,在我们实际的开发中不注意的话就会存在各种超卖。
窗口1售出第3张票
窗口2售出第2张票
窗口3售出第1张票
窗口2售出第0张票
窗口1售出第-1张票
要解决以上线程问题,只要在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。
1、synchronized关键字
类对象: 类的class文件
类的实例对象: 表示的类的本身实例
2、同步锁ReenreantLock
提供了比synchronized代码块和synchronized方法更广泛的锁定操作。
1、lock() //添加同步锁
2、unlock() //释放同步锁
Synchronized和Lock比较
1、Synchronized是关键字,内置语言实现,Lock是接口。
2、Synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。Lock异常时不会自动释放锁,所以需要在finally中实现释放锁。
3、Lock可以使用读锁提高多线程读效率。
4、用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了。
(2)线程通讯
线程间的通讯的常用方法
1、休眠唤醒方式:
Object的wait、notify、notifyAll
Condition的await、signal、signalAll
2、CountDownLatch:用于某个线程A等待若干个其他线程执行完之后,它才执行
3、CyclicBarrier:一组线程等待至某个状态之后再全部同时执行
4、Semaphore:用于控制 对某组资源的访问权限
1、休眠唤醒方式
我们用两条线程配合打印奇偶数:
public class myText {
private int a=0;
private Lock lock=new ReentrantLock();
private Condition oddcondition = lock.newCondition();
private Condition evencondition = lock.newCondition();
public void odd() { //打印奇数
// lock.lock();
synchronized(this){
try {
while (a <= 10) {
if (a % 2 == 0) {
//oddcondition.await(); //lock方式等待
this.wait(); //obj方式
} else {
System.out.println(Thread.currentThread().getName() + "打印:" + a);
a++;
//evencondition.signal(); //lock方式唤醒
this.notifyAll(); //obj方式
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//lock.unlock();
}
}
}
public void even() { //打印偶数
//lock.lock();
synchronized(this){
try {
while (a <= 10) {
if (a % 2 == 1) {
//evencondition.await();
this.wait();
} else {
System.out.println(Thread.currentThread().getName() + "打印:" + a);
a++;
//oddcondition.signal();
this.notifyAll(); //obj方式
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//lock.unlock();
}
}
}
public static void main(String[] args) {
myText myText = new myText();
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
myText.odd();
}
},"奇数线程");
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
myText.even();
}
},"偶数线程");
thread.start();
thread1.start();
}
}
结果:
偶数线程打印:0
奇数线程打印:1
偶数线程打印:2
奇数线程打印:3
偶数线程打印:4
......
通过wait和notifyall之间的配合实现线程按照我们的意愿执行。
1、object wait()必须在synchronized(同步锁)下使用,
2、object wait()必须要通过Nodify()方法进行唤醒
3、condition await() 必须和Lock(互斥锁/共享锁)配合使用
4、condition await() 必须通过 signal() 方法进行唤醒
2、CountDownLatch方式
java1.5被引入的,存在于java.util.concurrent包下,能够使一个线程等待其他线程完成各自的工作后再执行。通过一个计数器来实现的,计数器的初始值为线程的数量。
每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
我们将上面的代码改造:
public class myText {
private int a=0;
private Lock lock=new ReentrantLock();
private Condition oddcondition = lock.newCondition();
private Condition evencondition = lock.newCondition();
private CountDownLatch countDownLatch=new CountDownLatch(1); //定义CountDownLatch
public void odd() { //打印奇数
// lock.lock();
try {
while (a <= 10) {
if (a % 2 == 0) {
//oddcondition.await(); //等待
countDownLatch.await();
} else {
System.out.println(Thread.currentThread().getName() + "打印:" + a);
a++;
//evencondition.signal(); //唤醒
countDownLatch.countDown(); //减一
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//lock.unlock();
}
}
public void even() { //打印偶数
//lock.lock();
try {
while (a <= 10) {
if (a % 2 == 1) {
//evencondition.await();
countDownLatch.await();
} else {
System.out.println(Thread.currentThread().getName() + "打印:" + a);
a++;
//oddcondition.signal();
countDownLatch.countDown();
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//lock.unlock();
}
}
public static void main(String[] args) {
myText myText = new myText();
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
myText.odd();
}
},"奇数线程");
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
myText.even();
}
},"偶数线程");
thread.start();
thread1.start();
}
}
3、CyclicBarrier
在java1.5被引入的,存在于java.util.concurrent包下。实现让一组线程等待至某个状态之后再全部同时执行。
public class myText {
public static void main(String[] args) {
final CyclicBarrier cb=new CyclicBarrier(3); //定义线程数量
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println("1号准备。。。");
cb.await(); //减一
System.out.println("1号结束。。。");
}catch (Exception e){
e.printStackTrace();
}
}
});
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println("2号准备。。。");
cb.await(); //减一
System.out.println("2号结束。。。");
}catch (Exception e){
e.printStackTrace();
}
}
});
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(1000);
System.out.println("3号准备。。。");
cb.await(); //减一
System.out.println("3号结束。。。");
}catch (Exception e){
e.printStackTrace();
}
}
});
thread.start();
thread2.start();
thread3.start();
}
}
4、Semaphore
用于控制对某组资源的访问权限。
- **acquire:**请求
- **release:**释放
public class myText {
static class Machine implements Runnable{
private int num;
private Semaphore semaphore;
public Machine(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
public void run() {
try {
semaphore.acquire();//请求机器
System.out.println("工人"+this.num+"请求机器,正在使用机器");
Thread.sleep(1000);
System.out.println("工人"+this.num+"使用完毕,已经释放机器");
semaphore.release();//释放机器
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
int worker = 8;//工人数
Semaphore semaphore = new Semaphore(3);//机器数
for (int i=0; i< worker; i++){
new Thread(new Machine(i, semaphore)).start();
}
}
}
工人2请求机器,正在使用机器
工人0请求机器,正在使用机器
工人3请求机器,正在使用机器
工人0使用完毕,已经释放机器
工人3使用完毕,已经释放机器
工人2使用完毕,已经释放机器
工人7请求机器,正在使用机器
工人6请求机器,正在使用机器
工人4请求机器,正在使用机器
工人6使用完毕,已经释放机器
工人1请求机器,正在使用机器
工人4使用完毕,已经释放机器
工人7使用完毕,已经释放机器
工人5请求机器,正在使用机器
工人5使用完毕,已经释放机器
工人1使用完毕,已经释放机器
完美控制了资源的访问权限。