package com.qiku.yrc.work24; public class W1 { /** * 1、 使用synchronized 、wait() 、notifyAll()方法实现 生产者消费者模型 */ private static int number=0; private static final int FULL = 30; private static String LK = "LOCK"; class Producer implements Runnable{ @Override public void run() { while (true){ synchronized (LK){ if (number==FULL) { try { LK.wait(); //就绪状态 } catch (InterruptedException e) { e.printStackTrace(); } } number++;//生产商品 System.out.println(Thread.currentThread().getName() + "生产者生产,目前共有商品的个数: " + number); LK.notifyAll();//唤醒消费者线程 } }} } class Consumer implements Runnable{ @Override public void run() { while(true){ synchronized (LK){ if (number==0) { try { LK.wait(); //就绪状态 } catch (InterruptedException e) { e.printStackTrace(); } } number--;//生产商品 System.out.println(Thread.currentThread().getName() + "生产者生产,目前共有商品的个数: " + number); LK.notifyAll();//唤醒消费者线程 } }} } public static void main(String[] args) { W1 w1 = new W1(); Producer producer = w1.new Producer();//创建生产者对象 Consumer consumer = w1.new Consumer();//创建消费者对象 Thread t1 = new Thread(producer);// 生产者线程 Thread t2 = new Thread(consumer);// 消费者线程 t1.start(); t2.start(); } }
package com.qiku.yrc.work24; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; //使用3种不同的方式创建线程 public class W2 extends Thread{ public static void main(String[] args) throws Exception { W2 t1= new W2(); W2 t2 = new W2(); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("方法一 结束 "); //方法二 匿名内部类 创建 new Thread(new Runnable() { int sum2=0; public void run() { for (int i = 0; i < 1000; i++) { sum2+=i; } System.out.println(Thread.currentThread().getName()+" 1000以内的和为:" + ""+sum2); } }).start(); // new Thread() { // @Override // public void run() { // for (int i = 0; i < 1000; i++) { // System.out.println( // //获取线程的名字 // Thread.currentThread().getName() + i); // } // } // }).start(); //} //} // 方法三 Callable 创建MyCallable 、FutureTask对象 支持 泛型 重写 call方法 Mathod3 mathod3 = new Mathod3(); //将myCallable 指定的任务 交给 task FutureTask<Integer> task= new FutureTask<>(mathod3); //使用task为参数,创建Thread对象 Thread thread3 = new Thread(task); thread3.start(); } @Override public void run() { super.run(); int sum=0; for (int i = 0; i < 100; i++) { sum+=i; } System.out.println(Thread.currentThread().getName()+" 100以内的和为:" + ""+sum); } } class Mathod3 implements Callable<Integer>{ @Override public Integer call() throws Exception { int sum3=0; for (int i = 0; i < 100; i++) { sum3+=i; } System.out.println(Thread.currentThread().getName()+" 100以内的和为:" + ""+sum3); return sum3; } }
package com.qiku.yrc.work24; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; // * 3、 测试3种不同线程池,体会下区别 public class W3 { public static void main(String[] args) { //可存储的缓冲池 ExecutorService executorService = Executors.newCachedThreadPool(); W3 w3 = new W3();//创建线程对象 //向线程池中 布置任务 //提交 executorService.submit((Runnable) w3); //关闭线程池 executorService.shutdown(); } }
package com.qiku.yrc.work24; public class W4 { // * 4、 默写出线程的线程的生命周期,以及不同状态做什么事情? public static void main(String[] args) { // 3 线程的相关的方法 // //```java // Thread() // 线程的无参构造器 // Thread(String name) // 可以指定线程名字的构造器 // Thread(Runable target) // 根据自定义的对象 创建线程 // // void run() // 线程启动后具体执行的方法 // void start() // 启动线程的方法 ,使线程进入就绪状态 等待被分配时间片 //``` // //## 4、线程的生命周期 // //```java // 新建状态new - 使用new关键字创建之后进入的状态,此时线程并没有开始执行。 // 就绪状态runable - 调用start方法后进入的状态,此时线程还是没有开始执行。 // 运行状态running - 使用线程调度器调用该线程后进入的状态,此时线程开始执行, // 当线程的时间片执行完 毕后任务没有完成时回到就绪状态。 // 消亡状态dead - 当线程的任务执行完成后进入的状态,此时线程已经终止。 // 阻塞状态blocked - 当线程执行的过程中发生了阻塞事件进入的状态,如:sleep方法。 // 阻塞状态解除后进入就绪状态。 // // ------- // 使用new关键字创建线程对象 - 线程进入 新建状态 --- 不执行 // 调用start() - 线程进入 就绪状态(等待被分配时间片) --- 不执行 // 当线程分配到了时间时间片 - 线程进入 运行状态 ---- 执行run()方法 // 当时间片使用完毕之后,如果线程线程没结束 则回到 就绪状态(等待被分配时间片) --- 不执行 // 如果线程任务执行完毕 - 线程进入 消亡状态 // // 在运行状态时,若发生导致阻塞事件(sleep(times) , wait() ,join() ) // - 线程进入 阻塞状态(等待阻塞结束) -- 不执行 // 当阻塞状态结束时,则进入 就绪状态(等待被分配时间片) --- 不执行 // // // //``` // //## 5、获取线程的信息 & 常用方法 // //```java // long getId() // 获取线程的ID 编号 // String getName() // 获取线程的名字 // void setName(String name ) // 设置线程的名字 // static Thread currentThread() // 获取正在运行的线程对象 // // ----- // // static void yield() // 使线程进入 就绪状态(等待被分配时间片) 不是阻塞 // static void sleep(long times) //使线程进入休眠(阻塞)状态 // int getPriority() //获取线程的优先级 默认 5 // void setPriority(int newPriority) //设置优先级 优先级越高 不一定先执行,只是获取时间片的机会更多 // // void join() // 等待该线程结束 // void join(long times) // 等待该线程times毫秒 // // boolean isDaemon() // 判断是否是守护线程 // void setDaemon(boolean on)// 设置守护线程 //``` // //## 6 用户线程和守护线程 // //```java // 正常我们创建的线程 都是用户线程 // 用户线程和守护线程基本没什么区别 一样的 // 守护线程:唯一的区别,就是它会等其他线程全部结束之后,守护也会结束(不论守护线程是否执行完毕) // 也就是如果内存中只剩下守护线程,守护线程则会立刻结束 // // // 如果希望某个线程最后结束,可以设置为守护线程 // t.setDaemon(true); // 注意:必须在线程启动之前设置,否则会报异常 IllegalThreadStateException //``` // // // //## 7 线程同步机制 // //```java // 当多个线程同时访问同一种共享资源时,可能会造成数据的覆盖等不一致性问题, // 此时就需要对线程之间进行通信和协调,该机制就叫做线程的同步机制。 // // 多个线程并发读写同一个临界资源时会发生线程并发安全问题。 // // 异步操作:多线程并发的操作,各自独立运行。 // // 同步操作:多线程串行的操作,先后执行的顺序。 // // ------ // // 由程序结果可知:当两个线程同时对同一个账户进行取款时,导致最终的账户余额不合理。 // // 引发原因:线程一执行取款时还没来得及将取款后的余额写入后台,线程二就已经开始取款。 // // 解决方案:让线程一执行完毕取款操作后,再让线程二执行即可,将线程的并发操作改为串行操作。 // // 经验分享:在以后的开发尽量减少串行操作的范围,从而提高效率。 // // // // // 原子性: // 取钱步骤: 1、获取余额 2、余额扣减 3、修改余额 // 上面3个步骤,在程序的执行过程中,是可以互相独立 不具备原子性 // 原子性是说:把这单个步骤当成一个整体,要都执行,要么一个也不执行 // // 在Java语言中使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性, // // 具体方式如下: // 1、使用同步代码块的方式实现部分代码的锁定,格式如下: // synchronized(对象名) { // 编写所有需要锁定的代码; // 语句1 // 语句2 // 语句3 // } // 对象名 : 如果仅仅锁定 一个代码块,写什么对象的名字 都行 // 如果想锁定多个代码块 ,只需要写同一个对象的名字即可(保证是同一个对象锁) // // // 2、使用同步方法的方式实现所有代码的锁定。 直接使用synchronized关键字来修饰整个方法即可 // public synchronized void show(){ // //方法体 // } // // // 该方式等价于: // synchronized(this) { // 整个方法体的代码 // } // // // 当我们对一个静态方法加锁,如: // public synchronized static void xxx(){ // .... // } // 那么该方法锁的对象是类对象。每个类都有唯一的一个类对象。获取类对象的方式:类名.class。 // 静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的。 // 原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象。 // // class Peroson{} // // Person p1 = new Person(); // p1 ===> Peroson类的 对象 (可以有无数多个) // Perosn.class ===> Person类的 类对象 (唯一性) // // // //``` // // // //### 注意事项 // //```java // 使用synchronized保证线程同步应当注意: // 多个需要同步的线程在访问同步块时,看到的应该是同一个锁对象引用。 // 在使用同步块时应当尽量减少同步范围以提高并发的执行效率。 // // // StringBuffer类是线程安全的类,但StringBuilder类不是线程安全的类。 // Vector类和 Hashtable类是线程安全的类,但ArrayList类和HashMap类不是线程安全的类。 // Collections.synchronizedList() 和 Collections.synchronizedMap()等方法实现安全。 //``` } }