线程同步
1.临界资源问题:
在同一个进程中有多个线程执行同一任务,有一个共享资源,当一个线程操作 共享资源时,还没来得及修改,另一个线程把cpu时间片抢去了,又来操作共享资源,这时就出现.临界资源问题.
2.解决临界资源问题,要用到线程同步.
3.线程同步:
让想一起执行代码绑定成一个代码块,一个线程进去执行这个代码块,其他线程就 不能进去,只能在外面等待,等待代码块中线程执行完了,让代码块共享资源让出来, 哪个 线程抢到cpu时间片就可以进入代码块中执行了,这就是线程同步.
4.线程同步的方式:
4.1:同步代码块:有一个线程进入同步代码块自动上锁,这个线程对象把同步代码块中代码执行完了就会自动解锁.
4.1.1:语法:synchronized (锁对象) {
想要一起执行代码块;
}
4.1.2:同步代码块锁对象可以是任何对象,只要这个锁对象是这多个线程共享,锁对象的值一般不变.
4.1.3:同步代码块中锁范围越小越好.
eg:public class MyRunnable implements Runnable {
//声明一个成员变量存票数
public int ticket=1000;
//创建一个锁对象,所有线程共享
Object ob=new Object();
/**
* 实现父类的任务方法
*/
@Override
public void run() {
while (true) {//ticket=1
//1,2,3
//同步代码块
synchronized (ob) {//1,2
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket+"张票");
ticket--;
}else {
System.out.println(Thread.currentThread().getName()+"票已经销售完 毕");
break;
}
}
}
}
}
4.2:同步方法:将要一起执行代码放在同一个同步方法中,当一个线程进入同步方法会自 动上锁,当这个线程将同步方法中代码全部执行完了,就会自动解锁,此时其他线程抢到cpu时间片进入同步方法.
4.2.1:同步方法:public synchronized void 方法名(形参列表){
方法体;
}
4.2.2:锁的范围越小越好.
eg:public class MyRunnable implements Runnable {
//声明一个成员变量存票数
public int ticket=1000;
/**
* 实现父类的任务方法
*/
@Override
public void run() {
while (true) {//ticket=1
if (saleTicket()) {
break;
}
}
}
/**
* 同步方法
*/
private synchronized boolean saleTicket() {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"正在销售第"+ticket+"张票");
ticket--;
return false;
}else {
System.out.println(Thread.currentThread().getName()+"票已经销售完毕");
return true;
}
}
}
4.3:对象互斥锁:要手动上锁,还要确保在任意情况下能解锁.
4.3.1:语法:
//创建对象互斥锁的对象
Lock l=new ReentrantLock();
//上锁
l.lock();
//解锁
l.unlock();
4.3.2:锁范围越小越好.
4.3.3:对象互斥锁因为是手动上锁,所以一定要确保在任何情况下能解锁,否则会出现死锁现象.
5.线程同步:保证数据的安全性.
线程的安全性越高,效率越低.线程的安全性越低,效率越高.
优点:保证数据的安全性.
缺点:效率低(线程不光要捡到CPU时间片,还要抢到锁才能进行锁范围去执行,所以等待的时间更久,效率低).
设计模式
Java中有6大设计原则,23种设计模式.还有一种,有人归纳为设计原则,有人归纳为设计模式.
1.设计原则(扩展):
前人经过经验的总结出写Java代码的规则,平时写代码遵循这些规则,写出 代码就规范性好.java要求程序高内聚低耦合.
内聚性:独立性.
耦合度:依赖性.
1.1:单一职能原则:一个类一做一件事.
作用:提高程序内聚性,降低程序耦合度.
1.2:里氏替换原则:所有使用父类父接口的地方,都可以用子类去替换.
作用:提高程序可扩展性.
1.3:依赖倒置原则:能依赖抽象的就不依赖于具体的.
作用:提高程序可扩展性.
1.4:接口隔离原则:一个接口只被设计一种功能.
作用:提高程序灵活性和可扩展性.
1.5:迪米特原则:一个对象应该对其他对象保持最少的了解.
作用:提高程序内聚性,降低程序耦合度.
1.6:开闭原则:对扩展开发,对修改关闭.
作用:提高程序的可维护性.
2.设计模式:
前人经过项目经验的总结,总结出对特定问题的特定解决方案.
3.单例模式:一个类只有一个对象.
优点:减少频繁创建对象所消耗系统资源.
实现方式:私有化构造方法,声明私有静态当前对象变量,给他提供一个公有的静态的获得对象的方法.
3.1:饿汉式单例模式:相懒汉式单例模式来说,优点:在多线程中使用安全.缺点:耗系统资源.
eg:public class King {
private String sname;
private int wifeCount;
/**
* 私有化构造方法
*/
private King() {
}
/**
* 声明私有这个类的对象变量
*/
private static King k1=new King();
/**
* 公有的静态获得对象的方法
* @return
*/
public static King getKing() {
return k1;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getWifeCount() {
return wifeCount;
}
public void setWifeCount(int wifeCount) {
this.wifeCount = wifeCount;
}
}
3.2:懒汉式单例模式:相对于饿汉式单例模式:优点:不耗资源.缺点在多线程中使用不安全.如果想在多线程中使用就用线程同步.
eg:public class Boss {
private String name;
private double money;
/**
* 私有构造方法
*/
private Boss() {
}
/**
* 私有的静态的对象的变量
*/
private static Boss b1;
/**
* 公有的静态的获得对象的方法
* @return Boss
*/
public synchronized static Boss getBoss() {
if (b1==null) {
b1=new Boss();
}
return b1;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
4.生产者和消费者模式:
如果没有商品,生产者就要生产商品,如果有商品,生产者必须等待,等待这个商品被消费才能再生产.如果没有商品,消费者就要等待;如果有商品,消费者就要消费商品.
优点:安全性高.
缺点:效率低.
注意:监视器要监视所有线程,所以一般要是所有线程共用的一个对象,而我们的锁对象刚好是监视所有的线程,所以可以用锁对象作监视器来监视所有线程.
eg:public class Goods {
private int gid;
private String gname;
@Override
public String toString() {
return "Goods [gid=" + gid + ", gname=" + gname + "]";
}
public int getGid() {
return gid;
}
/**
* 实现商品编号自增
*/
public void setGid() {
this.gid++;
}
public String getGname() {
return gname;
}
public void setGname(String gname) {
this.gname = gname;
}
}
public class Consumer implements Runnable{
//声明商品变量
public Goods g;
/**
* 通过构造方法将商品对象传过来
* @param g
*/
public Consumer(Goods g) {
this.g=g;
}
/**
* 消费者的任务方法
*/
@Override
public void run() {
while (true) {
synchronized (Test2.ob2) {
//判断商品是否存在
if (Test2.flag==false) {//商品不存在,
try {
//等待
Test2.ob2.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {//商品存在,消费
System.out.println(Thread.currentThread().getName()+"在消费"+this.g);
//改变商品标记
Test2.flag=false;
//唤醒所有生产者
Test2.ob2.notifyAll();
}
}
}
}
}
public class Producer implements Runnable{
//声明商品变量
public Goods g;
/**
* 用构造方法将商品对象传过来
* @param g
*/
public Producer(Goods g) {
this.g=g;
}
/**
* 生产者的任务方法
*/
@Override
public void run() {
while (true) {//1,2,3
synchronized (Test2.ob2) {//1
//判断商品是否存在
if (Test2.flag==false) {//商品不存在,生产商品
//生产商品
this.g.setGid();
this.g.setGname("衣服");
System.out.println(Thread.currentThread().getName()+"在生产"+this.g);
//改变商品标记
Test2.flag=true;
//唤醒所有消费者去消费
Test2.ob2.notifyAll();
}else {//商品存在
try {
Test2.ob2.wait();//让当前所有生产者来等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
if (this.g.getGid()==100) {
break;
}
}
}
}
public class Test2 {
/**
* 声明一个变量作标记,标记当前的商品是否存在
*/
public static boolean flag=false;//默认不存在
/**
* 创建一个生产者锁对象,也作为生产者监视器,监视器要监视所有对象
*/
public static Object ob2=new Object();
public static void main(String[] args) {
//声明一个商品对象
Goods g=new Goods();
//创建生产者任务对象
Producer p1=new Producer(g);
//创建三个生产者
Thread t1=new Thread(p1, "张三1");
Thread t2=new Thread(p1, "张三2");
Thread t3=new Thread(p1, "张三3");
//创建消费都任务对象
Consumer c1=new Consumer(g);
//创建三个消费者
Thread t4=new Thread(c1, "李四1");
Thread t5=new Thread(c1, "李四2");
Thread t6=new Thread(c1, "李四3");
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}