线程同步(以下介绍三种方)
线程的职责就是执行一些操作,而多数操作都涉及到处理数据。这里有一个程序处理实例变量a:
a+=i;
a-=i;
System.out.println(a);
线程同步的特征:
- 如果一个同步代码块和非同步代码块同时操纵共享资源,仍然会造成对共享资源的竞争。因为当一个线程执行一个对象的同步代码块时,其他线程仍然可以执行对象的非同步代码块。
- 每个对象都有唯一的同步锁。
在静态方法前面也可以使用synchronized修饰符。此时该同步锁的对象为类对象(类的Class对象)。
- 当一个线程开始执行同步代码块时,并不意味着必须以不中断的方式运行。进入同步代码块的线程也可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并没有释放锁,只是把运行机会(即CPU)让给了其他的线程。
- synchnozied声明不会被继承。
同步是解决共享资源竞争的有效手段。当一个线程已经在操纵共享资源时,其他共享线程只能等待。为了提升并发性能,应该使同步代码块中包含尽可能少的操作,使得一个线程能尽快释放锁,减少其他线程等待锁的时间。
多个线程在操纵共享资源——实例变量时,有可能引起共享资源的况争。为了保证每个线程能正常执行操作,保证共享资源能正常访问和修改。Java引入了同步进制,具体做法是在有可能引起共享资源竞争的代码前加上synchronized标记。这样的代码被称为同步代码块。
每个Java对象 。当一个线程试图执行带有synchronized标记的代码块时,该线程必须首先获得this关键字引用的对象的锁。
. 如果这个锁已经被其他线程占用,Java虚拟机就会把这个线程放到this指定对象的锁池中,线程进入阻塞状态。在对象的锁池中可能会有许多等待锁的线程。等到其他线程释放了锁,Java虚拟机会从锁池中随机取出一个线程,使这个线
程拥有锁,并且转到就绪状态。
. 假如这个锁没有被其他线程占用,线程就会获得这把锁,开始执行同步代码块。在一般情况下,线程只有执行完同步代码块,才会释放锁,使得其他线程能够获得锁。
如果一个方法中的所有代码都属于同步代码,则可以直接在方法前用synchronized修饰。
public synchronized String pop(){...}
等价于
public String pop(){
synchronized(this){...}
}
1、同步”代码块“(就是将一整块代码同步)
public class AppleDemo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Apple a =new Apple();
Thread thread1 = new Thread(a,"A");
Thread thread2 = new Thread(a,"B");
Thread thread3 = new Thread(a,"C");
thread1.start();
thread2.start();
thread3.start();
}
}
class Apple implements Runnable{
private int appleCount = 50;
public void run(){
for(int i=0;i<50;i++){
//同步代码块锁要是唯一的,这里的this在整个代码里都是唯一的。
//这里this等于上面的a,也就是一般将共享的线程设为锁对象
synchronized(this){
if(appleCount>0){
System.out.println(Thread.currentThread().getName()+"吃苹果 "+appleCount--);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
2、同步“方法”
public class AppleDemo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Apple a =new Apple();
Thread thread1 = new Thread(a,"A");
Thread thread2 = new Thread(a,"B");
Thread thread3 = new Thread(a,"C");
thread1.start();
thread2.start();
thread3.start();
}
}
class Apple implements Runnable{
private int appleCount = 50;
public void run(){
fangfa();
}
//就是把本来写在run()中的执行代码,抽象成synchronized修饰方法,然后在run()中调用即可
//同步“方法”,用synchronizedz修饰,也可以这样写synchronized public void fangfa()
//不过习惯于public synchronized void fangfa()写
//这里sunchronized依然有锁,默认对应类的字节码对象
public synchronized void fangfa(){
for(int i=0;i<50;i++){
if(appleCount>0){
System.out.println(Thread.currentThread().getName()+"吃苹果 "+appleCount--);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
3、锁机制
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo{
public static void main(String[] args) {
Banana a =new Banana();
Thread thread1 = new Thread(a,"A");
Thread thread2 = new Thread(a,"B");
Thread thread3 = new Thread(a,"C");
thread1.start();
thread2.start();
thread3.start();
}
}
class Banana implements Runnable{
private int bananaCount = 50;
//这里定义一个lock类的对象,不能new本身lock只能new他的实现类
public Lock lock = new ReentrantLock();
public void run(){
for(int i=0;i<50;i++){
lock.lock();//上锁操作
if(bananaCount>0){
System.out.println(Thread.currentThread().getName()+"吃香蕉 "+bananaCount--);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
lock.unlock();//开锁操作
}
}
}
}