wait/notify 机制
一个线程调用Object的wait()方法,使其线程被阻塞;
另一线程调用Object的notify()/notifyAll()方法,wiat()阻塞的线程继续执行。
方法一:通过继承runnable实现
Main函数中执行
public static void main(String[] args) throws InterruptedException
{
MyThreadRunnable threadRunnable = new MyThreadRunnable();
Thread t1 = new Thread(threadRunnable);
t1.setName("线程1");
Thread t2 = new Thread(threadRunnable);
t2.setName("线程2");
t1.start();
t2.start();
}
MyThreadRunnable 方法执行内容
public class MyThreadRunnable implements Runnable
{
int mVal = 0;
private Object Lock = new Object();
public void run()
{
synchronized (Lock)
{
for (; mVal < 100; mVal++)
{
try
{
Lock.notifyAll();
Lock.wait();
System.out.println("MyThreadRunnable " + Thread.currentThread().getName() + ":" + mVal);
} catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
Lock.notifyAll();
}
}
}
运行结果如下
通过Runnable接口创建线程的步骤如下:
1、定义实现Runnable接口的类,并实现该类中的run()方法。
2、建立一个Thread对象,并将实现的Runnable接口的类的对象作为参数传入Thread类的构造方法。
- 通过Thread类中的start()方法启动线程,并运行。
方法二:通过继承Thread类进行实现
main方法中创建两个线程
public static void main(String[] args) throws InterruptedException
{
MyThread5 thread9=new MyThread5();
thread9.setName("线程09");
MyThread5 thread10=new MyThread5();
thread10.setName("线程10");
thread9.start();
thread10.start();
}
在MyThread5中继承Thread 类
public class MyThread5 extends Thread
{
private static Object Lock = new Object();
private static int sTimes = 0;
@Override
public void run()
{
while (true)
{
if(sTimes==101)
{
break;
}
synchronized (Lock)
{
System.out.println(Thread.currentThread().getName() + " : " + sTimes);
sTimes++;
Lock.notify();
try
{
Lock.wait();
} catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
}
}
}
- 继承Thread类实现多线程:实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程
- 线程类已经继承Thread类了,就不能再继承其他类
- 多个线程不能共享同一份资源
wait/notify总结:
wait方法
在执行wait()方法前,当前线程必须已获得对象锁。调用它时会阻塞当前线程,进入等待状态,在当前wait()处暂停线程。
wait()方法执行后,会立即释放获得的对象锁。
notify 方法
在执行notify方法前,当前线程也必须已获得线程锁。
调用notify()方法后,会通知一个执行了wait()方法的阻塞等待线程,使该等待线程重新获得取到对象锁,然后继续执行wait()后面的代码。
与wait()方法不同,执行notify()后,不会立即释放对象锁,而需要执行完synchronized的代码块或方法才会释放锁,所以接收通知的线程也不会立即获得锁,也需要等待执行notify()方法的线程释放锁后再获取锁。
notifyAll()
通知所有等待状态的线程,用notifyAll唤醒所有线程。
Thread类中其他实现方法
方法三:通过synchronized控制方法来实现
public static void main(String[] args) throws InterruptedException
{
MyThreadStatic1 thread7 = new MyThreadStatic1();
thread7.setName("线程7");
MyThreadStatic1 thread8 = new MyThreadStatic1();
thread8.setName("线程8");
thread7.start();
thread8.start();
}
MyThreadStaitic方法继承Thread类
public class MyThreadStatic1 extends Thread
{
private static int sVal = 0;
public static synchronized void addSVal()
{
sVal++;
}
@Override
public void run()
{
while (sVal < 100)
{
if (sVal % 2 == 0)
{
if (Thread.currentThread().getName() == "线程7")
{
System.out.println(Thread.currentThread().getName() + ":" + (sVal));
addSVal();
} else
{
sleep();
}
} else
{
if (Thread.currentThread().getName() == "线程8")
{
System.out.println(Thread.currentThread().getName() + ":" + (sVal));
addSVal();
} else
{
sleep();
}
}
}
}
private void sleep()
{
try
{
Thread.sleep(0);
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
这种方法主要通过控制addSVal函数,来控制变量进而控制两个线程的输出顺序。
方法四:通过Lock锁来实现
通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读写锁。
Lock接口基本的方法:
static Lock lock=new ReetrantLock();
public static void addNumber(){ lock.lock();//加锁
try{
number++;
}finally{
lock.unlock();//释放锁
}
}
Main函数中的代码
public static void main(String[] args) throws InterruptedException
{
MyThreadStatic2 thread7 = new MyThreadStatic2();
thread7.setName("线程7");
MyThreadStatic2 thread8 = new MyThreadStatic2();
thread8.setName("线程8");
thread7.start();
thread8.start(); }
MyThreadStatic2函数中的主要代码
import java.util.concurrent.locks.ReentrantLock;
public class MyThreadStatic2 extends Thread
{
private static final ReentrantLock mLock = new ReentrantLock();
private static int sVal = 0;
@Override
public void run()
{
while (sVal < 100)
{
if (sVal % 2 == 0)
{
if (Thread.currentThread().getName() == "线程7")
{
System.out.println(Thread.currentThread().getName() + " : " + sVal);
addSVal();
} else
{
sleep();
}
} else
{
if (Thread.currentThread().getName() == "线程8")
{
System.out.println(Thread.currentThread().getName() + " : " + sVal);
addSVal();
}else
{
sleep();
}
}
}
}
private void addSVal()
{
mLock.lock();
sVal++;
mLock.unlock();
}
private void sleep()
{
try
{
Thread.sleep(0);
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
方法五:通过AtomicInterger来实现
public static void main(String[] args) throws InterruptedException
{
MyThreadStatic3 thread7 = new MyThreadStatic3();
thread7.setName("线程7");
MyThreadStatic3 thread8 = new MyThreadStatic3();
thread8.setName("线程8");
thread7.start();
thread8.start();
}
MyThreadStatic3中的内容
import java.util.concurrent.atomic.AtomicInteger;
public class MyThreadStatic3 extends Thread
{
private static final AtomicInteger sVal=new AtomicInteger();
@Override
public void run()
{
while (sVal.get() < 100)
{
if (sVal.get() % 2 == 0)
{
if (Thread.currentThread().getName() == "线程7")
{
System.out.println(Thread.currentThread().getName() + ":" + (sVal));
sVal();
} else
{
sleep();
}
} else
{
if (Thread.currentThread().getName() == "线程8")
{
System.out.println(Thread.currentThread().getName() + ":" + (sVal));
sVal();
} else
{
sleep();
}
}
}
}
private void sleep()
{
try
{
Thread.sleep(0);
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
private void sVal()
{
sVal.set(sVal.get()+1);
}
}
总结:
synchronized使用
Java多线程——synchronized使用详解_synchronized用法-CSDN博客
- synchronized修饰代码块
修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。
结论:
当多个并发线程(thread1和thread2)访问同一个对象(这个很重要)(ThreadSyn)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,其他线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。
当多个线程访问 不同对象 的同步代码块,线程访问各自同步代码块,线程不会阻塞,互不干扰。
指定要给某个对象加锁
当有一个明确的对象作为锁时,
public void method(someObject obj)
{
//obj 锁定的对象
{
//todo
}
}
2 synchronized修饰一个方法
Synchronized修饰一个方法很简单,就是在方法的前面加synchronized。
public synchronized void method(){
//todo
};
写法二:
public void method()
{
synchronized(this) {
// todo
}
}
注意事项
- synchronized关键字不能继承。
虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。
- 在定义接口方法时不能使用synchronized关键字。
- 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
3. synchronized修饰一个静态的方法
Synchronized也可修饰一个静态方法,
public synchronized static void method() {
// todo
}
静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。
总结:
- 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;
如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
- 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
- 实现同步是要很大的系统开销代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。