多线程(创建---多线程安全问题)

单线程 运行途中有Expection就不会运行,而且效率较低
多线程:java的多线程是抢占性调度,优先级高就先调用,优先级相同就随机调用

创建多线程的方法

方法一
1.创建类继承Thread类作为Thread的子类
2.子类重写run方法(创建线程任务)也就是线程需要做什么
3.在main()方法中创建子类对象
4.子类对象调用start()方法开启新线程
注意:
多次启动一个线程是违法的,特别是一个线程结束后重启这个线程,是不允许的

方法二
1.创建Runnable的实现类
2.重写run()方法
3.在main()方法中创建实现类的对象

/*假设Runnable的实现类为RunnableImp*/
RunnableImp ri=new Runnable();

4.在main方法中以Runnable的实现类对象为参数构造Thread对象

/*例:*/Thread t=new Thread(ri);

5.用Thread对象调用start()开启新线程

方法二优于方法一
原因:1.继承只能继承一个而实现可以实现多个接口,若继承了Thread则不能继承其他父类
2.增强了程序的拓展性
将设置任务和开启多线程分离(解耦)
用一个thread的start方法可以开启多个不同的run(构造时传递不同的实现类对象)

/*
例:加入RunnableImp1和RunnableImp2都为Runnable的实现类
但是两个实现类的run方法内容不同
*/
public static void main(Stirng args[]{
  RunnableImp1 ip1=new RunnableImp1();
  Thread T=new Thread(ip1);
  /*
  若想开启一个RunnableImp2的线程则可以
  Thread T=new Thread(new RunnableImp2 );
  T.strat();//开启一个新线程	
}

匿名内部类
作用:
简化代码
方法:
把子类继承父类,创建子类对象,重写父类方法一步完成
把实现类实现接口,创建实现类,重写接口方法一步完成
没有子类/接口的名字
格式:
new 父类/接口(){重写方法}

 public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()+"ycj");
                }
            }
        }.start();//开启多线程

        Runnable r=new Runnable(){//定义接口的实现类对象   多态
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()+"严昌敬");
                }
            }
        };
        new Thread(r).start();

        //Runnable的简单写法,不用创建Runnable对象接收
        new Thread(new Runnable(){//定义接口的实现类对象   多态
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()+"三爷");
                }
            }
        }).start();

方法

获取线程名称:

 public class Getname extends Thread {
    public Getname() {
    }
    @Override
    public void run() {
        //获取线程的名称
        /*
        方法一:直接获取线程名称
        String name = getName();
        System.out.println(name);
        */
        /*
        方法二 先获取当前线程,在通过线程获取线程名称
        Thread T=Thread.currentThread();//获取当前线程,静态方法
        System.out.println(T);
        String name= T.getName();
        System.out.println(name);
        */
        //方法二写成一句话
        System.out.println(Thread.currentThread().getName());
    }
}

让线程休眠

public static void main(String[] args) {
        for (int i = 0; i < 60; i++) {
            try{
            Thread.sleep(1000);}//使进程休眠1000毫秒,需要抛出异常
            catch (Exception e) {
            }
            System.out.println(i);
        }
    }

线程安全问题

若多个线程用一个共享数据,则在争夺cpu时可能产生重复或者不存在的情况
解决线程安全问题的方法

方法1.在继承的子类或者是实现类里
格式
sychronized(锁对象){
可能出现线程安全问题的代码
}
注意:
1.锁对象可以是任意的对象
2.锁对象不能重复
3.锁对象的作用
把同步代码块锁住,只让一个线程在同步代码块中执行

private int tickets=100;
//锁对象定义在外面,防止调用run方法时产生多个重复的锁对象
//任意对象
Object o=new Object();
@Override
public void run() {
  while (true) {
      synchronized (o) {
          if (tickets > 0) {
              try {
                  Thread.sleep(10);
              }
              catch (Exception e) {

              }
              System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
              tickets--;
          }
      }
  }

方法2
创建一个方法用sychronized修饰,方法体是可能出现多线程安全问题的代码(从开始调用共享数据开始)

public class RunnableImp2 implements Runnable{
    private int tickets=100;
    @Override
    public void run() {
        while(true)
            payticket();
    }
    public  synchronized void payticket(){
        if (tickets>0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {

            }
            System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
            tickets--;
        }
    }
}

方法三

java.util.concurrent.Locks.Lock接口
用lock锁处理线程安全问题的方法
lock实现提供了比sychronized方法更广泛的操作
1.在成员位置创建一个ReentrantLock()
1.在可能出现线程安全问题的代码前用调用.lock()方法
2.在可能出现线程安全问题的代码结束后用.unlock()解锁
例:

public class Demo01Lock extends Thread {
   private int tickets=100;
   Lock l=new ReentrantLock();//1.创建ReentrantLock对象 (多态)

   @Override
   public void run() {
      /*
   更好的方法
   */
       while(true) {
           l.lock();//2.在可能出现线程安全问题的代码前获取锁
           if (tickets > 0) {
               try {
                   Thread.sleep(10);
                   System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
                   tickets--;
               } catch (Exception e) {
                   e.printStackTrace();
               }
               finally {
                   //这样可以保证无论是否有异常的出现都会释放锁,增加效率
                   l.unlock();//3.释放锁
               }
           }
           if(tickets==0)
               break;
       }
   }

用lambda和匿名内部类重写父类/接口方法

格式:new 父类名/接口名(){
重写方法
}

用lambda重写父类/接口的方法比匿名内部类更简单
格式:
new 父类/接口 对象=(父类/接口)(参数列表)->{重写的方法}

在方法中重写就可以省略构造父类/接口对象的过程
直接()->{} (在方法中用lambda重写会隐含的构造父类/接口,且自己匹配需要重写的方法)

其实严格意义上不是构造父类和接口的对象,抽象类不能直接构造

lambda优化

参数列表:
若参数只有一个,则参数和其类型都可以省略不写(要么一起写要么一起不写)
若有多个参数,则只能省略其参数类型
方法体(且方法体中代码只能有一句):
若有return; 则{}和return和;都可以不写(要么一起写要么一起不写)
若无return则{}和;可以不写(要么一起写要么一起不写)

注意

只有当父类/接口方法只有一个时才能用lambda重写,否则无法自动匹配

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 多线程的读者-写者问题是一个经典的同步问题,涉及到多个读线程和写线程对共享数据的访问。以下是一个可能的Java多线程读者-写者问题的实现代码: ```java class ReaderWriter { int readers = 0; int writers = 0; int writeRequests = 0; public synchronized void startRead() throws InterruptedException { while (writers > 0 || writeRequests > 0) { wait(); } readers++; } public synchronized void endRead() { readers--; notifyAll(); } public synchronized void startWrite() throws InterruptedException { writeRequests++; while (readers > 0 || writers > 0) { wait(); } writeRequests--; writers++; } public synchronized void endWrite() { writers--; notifyAll(); } } class Reader implements Runnable { ReaderWriter readerWriter; public Reader(ReaderWriter readerWriter) { this.readerWriter = readerWriter; } public void run() { try { readerWriter.startRead(); // 读取共享数据的操作 Thread.sleep((int) (Math.random() * 1000)); readerWriter.endRead(); } catch (InterruptedException e) { e.printStackTrace(); } } } class Writer implements Runnable { ReaderWriter readerWriter; public Writer(ReaderWriter readerWriter) { this.readerWriter = readerWriter; } public void run() { try { readerWriter.startWrite(); // 写入共享数据的操作 Thread.sleep((int) (Math.random() * 1000)); readerWriter.endWrite(); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Main { public static void main(String[] args) { ReaderWriter readerWriter = new ReaderWriter(); for (int i = 0; i < 5; i++) { new Thread(new Reader(readerWriter)).start(); } for (int i = 0; i < 5; i++) { new Thread(new Writer(readerWriter)).start(); } } } ``` 在上面的代码中,`ReaderWriter`类是一个用于管理读者和写者访问共享数据的同步类。`startRead()`和`endRead()`方法用于读者访问共享数据的开始和结束,`startWrite()`和`endWrite()`方法用于写者访问共享数据的开始和结束。在每个方法中使用`synchronized`关键字来保证同一时间只有一个线程可以执行。`notifyAll()`方法用于唤醒其他正在等待的线程。 `Reader`类和`Writer`类分别是读者和写者线程的实现。在`run()`方法中,先调用对应的`startRead()`或`startWrite()`方法来获取访问权限,然后执行读取或写入操作,最后调用对应的`endRead()`或`endWrite()`方法来释放访问权限。 在`main()`方法中创建了5个读者线程和5个写者线程,并启动它们。 以上代码是一种可能的多线程读者-写者问题的实现,但并不是唯一的解决方案。在实际应用中,还需要考虑更多的场景和线程同步的细节,确保共享数据的一致性和线程的安全执行。 ### 回答2: Java多线程读者-写者问题可以通过使用synchronized关键字和wait()、notify()方法来实现。下面是一个简单的示例代码: ``` public class ReaderWriterProblem { private static final int MAX_READERS = 5; private static final int MAX_WRITERS = 2; private static int activeReaders = 0; private static boolean writerActive = false; public static void main(String[] args) { for (int i = 1; i <= MAX_READERS; i++) { new Thread(new Reader(i)).start(); } for (int i = 1; i <= MAX_WRITERS; i++) { new Thread(new Writer(i)).start(); } } static class Reader implements Runnable { private final int readerId; public Reader(int readerId) { this.readerId = readerId; } @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } read(); } } private void read() { synchronized (ReaderWriterProblem.class) { while (writerActive) { // 如果有写者在执行 try { ReaderWriterProblem.class.wait(); // 等待写者完成 } catch (InterruptedException e) { e.printStackTrace(); } } activeReaders++; // 增加活跃读者数量 } // 执行读操作 System.out.println("Reader " + readerId + "正在执行读操作"); synchronized (ReaderWriterProblem.class) { activeReaders--; // 减少活跃读者数量 if (activeReaders == 0) { // 如果没有其他读者 ReaderWriterProblem.class.notifyAll(); // 唤醒所有等待的线程 } } } } static class Writer implements Runnable { private final int writerId; public Writer(int writerId) { this.writerId = writerId; } @Override public void run() { while (true) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } write(); } } private void write() { synchronized (ReaderWriterProblem.class) { while (activeReaders > 0 || writerActive) { // 如果有读者在执行或者有写者在执行 try { ReaderWriterProblem.class.wait(); // 等待其他线程完成操作 } catch (InterruptedException e) { e.printStackTrace(); } } writerActive = true; } // 执行写操作 System.out.println("Writer " + writerId + "正在执行写操作"); synchronized (ReaderWriterProblem.class) { writerActive = false; ReaderWriterProblem.class.notifyAll(); // 唤醒所有等待的线程 } } } } ``` 在上述代码中,我们使用了一个整型变量`activeReaders`来记录当前活跃的读者数量,当读者开始读操作时,会先判断是否有写者在执行,如果有,则等待写者完成;然后增加`activeReaders`;接着执行读操作。读操作完成后,减少`activeReaders`,如果没有其他读者,则调用`notifyAll()`方法唤醒其他等待的线程。写者也类似,当写者开始写操作时,会先判断是否有其他读者或写者在执行,如果有,则等待其他线程完成;然后执行写操作;最后,设置`writerActive`为false,并调用`notifyAll()`方法唤醒其他等待的线程。 这种方式实现的读者-写者问题可以保证同一时间只能有一个写者或多个读者执行操作,从而保证数据的一致性和并发访问的正确性。 ### 回答3: Java多线程读者-写者问题可以通过使用synchronized关键字和wait()、notify()方法实现。 首先,读者-写者问题是指多个读者线程可以同时读取数据,但写者线程只能在没有读者线程时才能写入数据。 下面是使用Java实现的一个基本读者-写者问题的代码示例: ```java public class ReaderWriter { private int readers; private boolean isWriting; public ReaderWriter() { readers = 0; isWriting = false; } public synchronized void startRead() throws InterruptedException { while (isWriting) { wait(); } readers++; } public synchronized void endRead() { readers--; if (readers == 0) { notifyAll(); } } public synchronized void startWrite() throws InterruptedException { while (isWriting || readers > 0) { wait(); } isWriting = true; } public synchronized void endWrite() { isWriting = false; notifyAll(); } } ``` 上面的代码中,ReaderWriter类用于管理读者和写者的访问。其中,startRead()方法用于读者开始读取数据,endRead()方法用于读者结束读取;startWrite()方法用于写者开始写入数据,endWrite()方法用于写者结束写入。 在startRead()和startWrite()方法中,使用synchronized关键字修饰,确保同一时间只能有一个线程访问该方法。在读者读取数据时,如果有写者在写入,则调用wait()方法使读者线程进入等待状态。在写者写入数据时,如果有其他读者在读取,也调用wait()方法使写者线程进入等待状态。 在endRead()和endWrite()方法中,对读者和写者的数量进行更新,并使用notifyAll()方法唤醒等待的线程。当所有读者都结束读取时,唤醒可能等待的写者线程;当写者结束写入时,唤醒可能等待的读者线程。 通过以上方法的实现,能够实现读者-写者问题多线程并发访问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值