Java线程同步于并发

来源:互联网 发布:www.windows.com 编辑:程序博客网 时间:2024/05/18 01:35
// 线程同步的特征
// 1,如果一个同步代码块和非同步代码块同时操作共享资源,仍然会造成对共享资源的竞争,因为一个线程执行一个对象的同步代码块时,其他线程仍然可以执行对象的非同步代码块
// 所谓线程之前的保持同步,是指不同的线程在执行同一个对象的同步代码块时,因为要获得这个对象的锁而相互牵制
// 2.每个对象都有唯一的同步锁
// 3.在静态方法前面也可以使用synchronized修饰符,每个被加载到java虚拟机的方法区的类也有唯一的同步锁
// 4.当一个线程开始执行同步代码块时,此时执行Thread.sleep()或者执行Thread.yield()方法,此时它并没有释放锁,只是把CPU让给了其他的线程,其他线程有机会获得CPU,进入运行状态,由于前者没有释放锁,所以其他线程依然在锁池中等待
// 5.synchronized声明不会被继承,如果一个用synchronized修饰的方法被子类覆盖,那么子类中这个方法不再保持同步,除非也用synchronized修饰


// 同步与并发
// 同步时解决资源竞争的有效手段,但是多线程的同步与并发是一对此消彼长的矛盾,为了提高并发性能,应该使同步代码块中包含尽可能少的操作,使得一个线程能尽快释放锁,减少其他线程等待锁的时间,
// 例如 for(int i = 0;i<10;i++){well.withdraw();yield();} class Well{public synchronized void withdraw(){}} 也就是不要在for循环外加上synchronized(well)这样其他线程就获取不到这个对象了
// 线程安全的类:1.这个类的对象可以同时被多个线程安全的访问,2.每个线程都能正常执行原子操作,得到正确的结果,3.每个线程的原子操作都完成后,对象处于逻辑上合理的状态
// 可变类的线程安全往往以降低并发性能为代价,为了减小这一负面影响,可以采取以下措施:
// 1.只对可能导致资源竞争的代码进行同步,例如Stack的name属性只提供了getName()方法,没有提供修改name属性的方法,Stack的name属性永远不会改变,当多个线程同时执行getName()方法时,不会导致资源竞争,所以不需用synchronized来修改getName方法
// 2.如果一个可变类有两种运行环境--单线程运行环境和多线程运行环境,那么可以提供两种实现,在单线程中使用未同步的类实现,在多线程运行环境中使用采取同步的类的实现,所谓单线程类的对象只会被一个线程访问,多线程运行环境指同一个对象会被多个线程同时访问,例如java的HashSet是线程不安全的
// Collections.synchronizedSet方法返回原始HashSet集合的同步版本,在多线程环境中,可以访问这个版本
// 释放对象的锁:
// 1.执行完同步代码块,就会释放锁, 2.在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放,3.在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个对象会释放锁,进入对象的等待池
// 不释放对象的锁:
// 1.在执行同步代码块过程中,执行了Thread.sleep()方法,当前线程进入睡眠,在睡眠中不会释放锁
// 2.在执行同步代码块过程中,执行了Thread.yield()方法,当前线程放弃CPU,但不会释放锁

// 3.在执行同步代码块过程中,其他线程执行了当前线程对象的suspend()方法,当前线程被暂停,但不会释放锁,这个方法已经被废弃了

public class Test11_1 {public static void main(String[] args) {HashSet<String> h= (HashSet<String>)Collections.synchronizedSet(new HashSet<String>());Stack stack = new Stack("stack1"); // 创建一个类对象Producer producer1 = new Producer(stack, "producer1"); // 创建一个操作这个对象属性的生产者Producer producer2 = new Producer(stack, "producer2"); // 创建一个操作这个对象属性的生产者Consumer consumer1 = new Consumer(stack, "consumer1"); // 创建一个操作这个对象属性的消费者}}class Producer extends Thread{private Stack theStack;public Producer(Stack s,String name){super(name); // 调用线程名称theStack = s; // 赋给同一对象start(); // 启动线程}public void run(){String goods;for(int i = 0;i<200;i++){synchronized (theStack) { // 对同一对象进行锁定操作,不加这个同步代码,会发生两个线程都执行了?+1,因为没有加锁,所以结果一样了,这是不对的goods="goods" +(theStack.getPoint()+1);theStack.push(goods); // 对theStack这个对象的point执行+1操作System.out.println(getName()+": push " + goods + " to " + theStack.getName());}yield(); // 将CPU使用权让给运行级别一样的线程}}}class Consumer extends Thread{private Stack theStack;public Consumer(Stack s, String name){super(name); // 调用线程名称theStack = s; // 赋给同一对象start(); // 启动线程}public void run(){String goods;for(int i = 0;i<200;i++){synchronized (theStack) { // 对同一对象进行锁定操作goods = theStack.pop(); // 对theStack这个对象的point执行-1操作System.out.println(getName()+": pop " + goods + " from " + theStack.getName());}yield(); // 将CPU使用权让给运行级别一样的线程}}}class Stack{private String name;private String[] buffer = new String[1000];int point = -1;public Stack(String name){this.name = name;}public String getName(){return name;}public synchronized int getPoint(){return point;}// 消费public synchronized String pop(){String goods = buffer[point];buffer[point]=null;Thread.yield();if(point>0){point--;}return goods;}// 生产public synchronized void push(String goods){point++;Thread.yield();buffer[point]=goods;}}


0 0
原创粉丝点击