什么是同步?
同步指的是所有的线程不是一起进入到方法中执行,而是按照顺序一个一个进来。
范例:多个线程同步卖票
class MyThread implements Runnable {
private int tickte = 10;
@Override
public void run() {
while (tickte > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "还剩" + this.tickte + "张票");
this.tickte--;
}
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread,"线程A");
Thread thread2=new Thread(myThread,"线程B");
Thread thread3=new Thread(myThread,"线程C");
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果:
观察上述代码的运行结果,我们发现票数居然出现了负数。为什么会这个样子呢?因为我们创建的三个线程同时卖票,他们会同时进入run()方法中,同时买一张票,假如说当现在还有2张票,但是由于三个线程同时卖一张票,线程A卖了一张票本来就剩一张票了,但是线程B和线程C都已经进入了while循环,还以为有两张票继续卖,这个操作叫做不同步操作。不同步的唯一好处是处理速度块(多线程并发执行)。
为了实现同步处理,我们可以进行加锁操作,采用关键字synchronized来处理。使用synchronized关键字处理有两种模式:同步代码块、同步方法。
如果要使用同步代码块必须设置一个要锁定的对象,所以一般可以锁定当前对象:this
范例:
class MyThread implements Runnable {
private int tickte = 10;//一共10张票
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//在同一时刻,只允许一个线程进入代码块处理
synchronized (this) {
if (tickte > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}//模拟网络延迟
System.out.println(Thread.currentThread().getName() + "还剩" + this.tickte + "张票");
this.tickte--;
}
}
}
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread,"线程A");
Thread thread2=new Thread(myThread,"线程B");
Thread thread3=new Thread(myThread,"线程C");
thread1.start();
thread2.start();
thread3.start();
}
}
范例:
class MyThread implements Runnable {
private int tickte = 10;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
sale();
}
}
//同步方法
public synchronized void sale(){
if (tickte > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "还剩" + this.tickte + "张票");
this.tickte--;
}
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread,"线程A");
Thread thread2=new Thread(myThread,"线程B");
Thread thread3=new Thread(myThread,"线程C");
thread1.start();
thread2.start();
thread3.start();
}
}
同步虽然可以保证数据的完整性(线程安全操作),但是其执行的速度会很慢
注解:看完了上述两种锁操作,你一定会有一个疑问就是----为什么只有上锁操作没有解锁操作呢?其实它有一个隐式解锁操作,就是执行完锁里面的代码后,会自动解锁。
范例:synchronized锁多对象
class Sync{
//锁的是当前对象this
public synchronized void test(){
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 implements Runnable {
@Override
public void run() {
Sync sync=new Sync();
sync.test();
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread);
Thread thread2=new Thread(myThread);
Thread thread3=new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果:
观察上述代码,我们发现没有锁住,三个线程同时运行test()方法。因为每一个进程在运行run()方法的时候都会new一个Sync新对象,所以synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段。即synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。
那怎么来解决上面这个问题呢?
class Sync{
//锁的是当前对象this
public synchronized void test(){
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 implements Runnable {
private Sync sync;
public MyThread(Sync sync){
this.sync=sync;
}
@Override
public void run() {
sync.test();
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread(new Sync());
Thread thread1=new Thread(myThread);
Thread thread2=new Thread(myThread);
Thread thread3=new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
全局锁有两种方式:使用类的静态同步方法和在代码块中锁当前Class对象。
synchronized与static一起使用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。
class Sync{
public static synchronized void test(){
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 implements Runnable {
@Override
public void run() {
Sync sync=new Sync();
sync.test();
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread);
Thread thread2=new Thread(myThread);
Thread thread3=new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
synchronized (类名称.Class){},这种全局锁,锁的是类而不是this
class Sync{
public void test() {
//锁的是类而不是this
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 implements Runnable {
@Override
public void run() {
Sync sync=new Sync();
sync.test();
}
}
class Test{
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread1=new Thread(myThread);
Thread thread2=new Thread(myThread);
Thread thread3=new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}