多线程(二)
来源:互联网 发布:天谕女角色捏脸数据 编辑:程序博客网 时间:2024/05/20 11:27
JDK5—Lock
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁, 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。 Lock: void lock(): 获取锁。 void unlock():释放锁。 ReentrantLock是Lock的实现类。
package cn.itcast_01;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable { // 定义票 private int tickets = 100; // 定义锁对象 private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { // 加锁 lock.lock(); if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); } } finally { // 释放锁 lock.unlock(); } } }}
package cn.itcast_01;public class SellTicketDemo { public static void main(String[] args) { // 创建资源对象 SellTicket st = new SellTicket(); // 创建三个窗口 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); // 启动线程 t1.start(); t2.start(); t3.start(); }}
死锁问题
同步的弊端: A:效率低 B:容易产生死锁 死锁: 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。 举例: 中国人,美国人吃饭案例。 正常情况: 中国人:筷子两支 美国人:刀和叉 现在: 中国人:筷子1支,刀一把 美国人:筷子1支,叉一把
package cn.itcast_02;public class MyLock { // 创建两把锁对象 public static final Object objA = new Object(); public static final Object objB = new Object();}
package cn.itcast_02;public class DieLock extends Thread { private boolean flag; public DieLock(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { synchronized (MyLock.objA) { System.out.println("if objA"); synchronized (MyLock.objB) { System.out.println("if objB"); } } } else { synchronized (MyLock.objB) { System.out.println("else objB"); synchronized (MyLock.objA) { System.out.println("else objA"); } } } }}
package cn.itcast_02;public class DieLockDemo { public static void main(String[] args) { DieLock dl1 = new DieLock(true); DieLock dl2 = new DieLock(false); dl1.start(); dl2.start(); }}
线程通信
分析: 资源类:Student 设置学生数据:SetThread(生产者) 获取学生数据:GetThread(消费者) 测试类:StudentDemo 问题1:按照思路写代码,发现数据每次都是:null---0 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 如何实现呢? 在外界把这个数据创建出来,通过构造方法传递给其他的类。 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 A:同一个数据出现多次 B:姓名和年龄不匹配 原因: A:同一个数据出现多次 CPU的一点点时间片的执行权,就足够你执行很多次。 B:姓名和年龄不匹配 线程运行的随机性 线程安全问题: A:是否是多线程环境 是 B:是否有共享数据 是 C:是否有多条语句操作共享数据 是 解决方案: 加锁。 注意: A:不同种类的线程都要加锁。 B:不同种类的线程加的锁必须是同一把。
package cn.itcast_04;public class Student { String name; int age;}
package cn.itcast_04;public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { if (x % 2 == 0) { s.name = "林青霞";//刚走到这里,就被别人抢到了执行权 s.age = 27; } else { s.name = "刘意"; //刚走到这里,就被别人抢到了执行权 s.age = 30; } x++; } } }}
package cn.itcast_04;public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { System.out.println(s.name + "---" + s.age); } } }}
package cn.itcast_04;public class StudentDemo { public static void main(String[] args) { //创建资源 Student s = new Student(); //设置和获取的类 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //线程类 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //启动线程 t1.start(); t2.start(); }}
等待唤醒机制
问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 如何实现呢? 通过Java提供的等待唤醒机制解决。 等待唤醒: Object类中提供了三个方法: wait():等待 notify():唤醒单个线程 notifyAll():唤醒所有线程 为什么这些方法不定义在Thread类中呢? 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 所以,这些方法必须定义在Object类中。
package cn.itcast_05;public class Student { String name; int age; boolean flag; // 默认情况是没有数据,如果是true,说明有数据}
package cn.itcast_05;public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { //判断有没有 if(s.flag){ try { s.wait(); //t1等着,释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } if (x % 2 == 0) { s.name = "林青霞"; s.age = 27; } else { s.name = "刘意"; s.age = 30; } x++; //x=1 //修改标记 s.flag = true; //唤醒线程 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。 } //t1有,或者t2有 } }}
package cn.itcast_05;public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { synchronized (s) { if(!s.flag){ try { s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //林青霞---27 //刘意---30 //修改标记 s.flag = false; //唤醒线程 s.notify(); //唤醒t1 } } }}
package cn.itcast_05;public class StudentDemo { public static void main(String[] args) { //创建资源 Student s = new Student(); //设置和获取的类 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //线程类 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //启动线程 t1.start(); t2.start(); }}
线程状态转换图
线程组
线程类里面的方法:public final ThreadGroup getThreadGroup() 线程组: 把多个线程组合到一起。 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 ThreadGroup(String name) Thread(ThreadGroup group, Runnable target, String name) public final String getName()
package cn.itcast_06;public class MyRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } }}
package cn.itcast_06;public class ThreadGroupDemo { public static void main(String[] args) { // method1(); // 我们如何修改线程所在的组呢? // 创建一个线程组 // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组 method2(); // t1.start(); // t2.start(); } private static void method2() { // ThreadGroup(String name) ThreadGroup tg = new ThreadGroup("这是一个新的组"); MyRunnable my = new MyRunnable(); // Thread(ThreadGroup group, Runnable target, String name) Thread t1 = new Thread(tg, my, "林青霞"); Thread t2 = new Thread(tg, my, "刘意"); System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName()); //通过组名称设置后台线程,表示该组的线程都是后台线程 tg.setDaemon(true); } private static void method1() { MyRunnable my = new MyRunnable(); Thread t1 = new Thread(my, "林青霞"); Thread t2 = new Thread(my, "刘意"); // 我不知道他们属于那个线程组,我想知道,怎么办 // 线程类里面的方法:public final ThreadGroup getThreadGroup() ThreadGroup tg1 = t1.getThreadGroup(); ThreadGroup tg2 = t2.getThreadGroup(); // 线程组里面的方法:public final String getName() String name1 = tg1.getName(); String name2 = tg2.getName(); System.out.println(name1); System.out.println(name2); // 通过结果我们知道了:线程默认情况下属于main线程组 // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组 System.out.println(Thread.currentThread().getThreadGroup().getName()); }}
最终版代码
package cn.itcast_07;public class Student { private String name; private int age; private boolean flag; // 默认情况是没有数据,如果是true,说明有数据 public synchronized void set(String name, int age) { // 如果有数据,就等待 if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 设置数据 this.name = name; this.age = age; // 修改标记 this.flag = true; this.notify(); } public synchronized void get() { // 如果没有数据,就等待 if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 获取数据 System.out.println(this.name + "---" + this.age); // 修改标记 this.flag = false; this.notify(); }}
package cn.itcast_07;public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { if (x % 2 == 0) { s.set("林青霞", 27); } else { s.set("刘意", 30); } x++; } }}
package cn.itcast_07;public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true) { s.get(); } }}
public class StudentDemo { public static void main(String[] args) { //创建资源 Student s = new Student(); //设置和获取的类 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //线程类 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //启动线程 t1.start(); t2.start(); }}
0 0
- 多线程同步(二)
- C++多线程(二)
- 多线程(二) Thread
- 多线程总结(二)
- 多线程(二)
- Qt多线程(二)
- linux多线程(二)
- c++多线程(二)
- C++多线程(二)
- 多线程学习(二)
- c++多线程(二)
- 多线程 笔记(二)
- java多线程(二)
- C++多线程(二)
- linux多线程(二) .
- GCD 多线程 (二)
- GCD 多线程 (二)
- c++多线程(二)
- Android下进行单元测试
- 初识HTML5
- 在C/C++代码中使用SSE等指令集的指令(4)SSE指令集Intrinsic函数使用
- 关于MOD
- UITextView & UITextField 实现中英文混合输入的限制
- 多线程(二)
- 想成为优秀的程序员你必须做到的几件事情
- 安卓wifi遥控源代码 ESP8266模块测试
- 在C/C++代码中使用SSE等指令集的指令(5)SSE进行加法运算简单的性能测试
- OOZIE JA017: Unknown hadoop job
- javascript中面向对象之原型链解析
- RDS
- Python标准库的方法
- SSH复习