未来可期。一位正行走在编程世界中的小白,希望能遇到更多正在努力中的小伙伴。
我以生活中的例子来打开这个问题,例如:我们做火车买票为例子。
创建个窗口,总票数为100张,使用实现Runable接口的方式
代码示例:
class Window1 implements Runnable{
//总票数
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
ticket--;
}else{
break;
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
语法:
synchronized(同步监视器){
// 需要不同的代码
}
说明:
代码示例:
class Window2 extends Thread{
//共享数据
private static int ticket = 100;
@Override
public void run(){
//添加同步代码块
// Class clazz =Window2.class,其中Window2.class只会加载一次
while(true){
synchronized(Window2.class){
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":卖票,票号为:"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class Window2Test {
public static void main(String[] args) {
Window2 t1 = new Window2();
Window2 t2 = new Window2();
Window2 t3 = new Window2();
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
代码示例:
class Window1 implements Runnable{
//总票数
private int ticket = 100;
@Override
public void run() {
while(true){
//设置同步代码块
// 此时的this:唯一的Window1的对象
synchronized(this){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
在实现Runable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
如果操作共享数据的代码完整的声明在一个方法中,我们不妨碍将此方法声明同步的
代码示例:
class Window3 extends Thread{
private static int ticket = 100;
@Override
public void run(){
while(true){
show();
}
}
private static synchronized void show(){ //同步监视器:Window3.class
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
ticket--;
}
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window3 w1 = new Window3();
Window3 w2 = new Window3();
Window3 w3 = new Window3();
w1.setName("窗口一");
w2.setName("窗口二");
w3.setName("窗口三");
w1.start();
w2.start();
w3.start();
}
}
class Window4 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
show();
}
}
private synchronized void show(){ //同步监视器:this
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
ticket--;
}
}
}
public class WindowTest4 {
public static void main(String[] args) {
Window4 w = new Window4();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
代码示例:
class Window5 implements Runnable{
// 1.实例化ReentrantLock
ReentrantLock lock = new ReentrantLock();
private int ticket = 100;
@Override
public void run() {
while(true){
try {
lock.lock();
// 2. 调用锁定方法lock()
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":售票,票号为:"+ticket);
ticket--;
}else{
break;
}
} finally {
// 3.调用解锁方法:unlock()
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window5 w = new Window5();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
使用的优先顺序
Lock --> 同步代码块(已经进入方法体,分配了相对应资源) --> 同步方法(在方法体之外)
利弊
synchronzied 与 Lock 的异同
死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
说明:
代码示例:
class A {
public synchronized void foo(B b) { //同步监视器:A类的对象:a
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 进入了A实例的foo方法"); // ①
// try {
// Thread.sleep(200);
// } catch (InterruptedException ex) {
// ex.printStackTrace();
// }
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 企图调用B实例的last方法"); // ③
b.last();
}
public synchronized void last() {//同步监视器:A类的对象:a
System.out.println("进入了A类的last方法内部");
}
}
class B {
public synchronized void bar(A a) {//同步监视器:b
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 进入了B实例的bar方法"); // ②
// try {
// Thread.sleep(200);
// } catch (InterruptedException ex) {
// ex.printStackTrace();
// }
System.out.println("当前线程名: " + Thread.currentThread().getName()
+ " 企图调用A实例的last方法"); // ④
a.last();
}
public synchronized void last() {//同步监视器:b
System.out.println("进入了B类的last方法内部");
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B();
public void init() {
Thread.currentThread().setName("主线程");
// 调用a对象的foo方法
a.foo(b);
System.out.println("进入了主线程之后");
}
@Override
public void run() {
Thread.currentThread().setName("副线程");
// 调用b对象的bar方法
b.bar(a);
System.out.println("进入了副线程之后");
}
public static void main(String[] args) {
DeadLock dl = new DeadLock();
new Thread(dl).start();
dl.init();
}
}
wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器。
notify():一旦执行方法,就会唤醒被wait的第一个线程,如果有多个线程被wait,就唤醒优先级最高的那个。
notifyAll():一旦执行此方法,就会唤醒所有被wait()的线程。
代码示例:
线程通信的例子:使用两个线程打印 1-100。线程1, 线程2 交替打印
class Number implements Runnable{
private int number = 1;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized(obj){
//唤醒线程
obj.notify();
if(number <= 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":" +number);
number++;
try {
// 使得调用如下wait()方法的线程进入阻塞状态
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
wait(),notify().notifyAll():三个方法必须用在同步代码块或同步方法中。
wait(),notify(),notifyAll():三个方法的调用者必须是同步代码块同步方法的同步监视器。否则会出现IllegalMonitorStateException异常
wait(),notify(),notifyAll():三个方法时定义在java.lang.Object类中。
① 当前线程的同步方法、同步代码执行结束。
② 当前线程在同步代码块、同步方法中遇到break、return终止该代码块、该方法的继承执行。
③ 当前线程在同步代码块、同步方法中出现未处理的Error或Exception,导致异常结束。
④当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
sleep()与wait()的异同
java多线程基础到这里结束了,在写博客同时一边复习自己所学的知识点,给自己不断积累知识点。